Text
                    СЕРИЯ
ОДЭКА
ОДЭКА
ПРОГРАММИРУЕМЫЕ СИСТЕМЫ
www.dodeca.ru
1Д
СО
ф
Ct
ф
\о
ф
3
2
Ш
X
X
ш
х
к
§
ф
к
ко
о
и
о
с
Компакт-диск
внутри
CodeVision AVR
Пособие для начинающих
Лебедев М.Б.

М. Б. Лебедев CodeVisionAVR Пособие для начинающих А Москва * Издательский дом «Додэка-ХХ1» ОАЭКА 2008
УДК 004.312.46 ББК 32.973.26-04-018.2 лзз Лебедев М. Б. ЛЗЗ CodeVisionAVR: пособие для начинающих. — М.: Додэка-ХХ1, 2008. — 592 с.: ил. ISBN 978-5-94120-192-1 В книге изложены основные приёмы работы в интегрированной среде разработки CodeVisionAVR, предназначенной для разработки программного обеспечения и программирования микроконтроллеров AVR на языке Си. Автор постарался сделать описание программы CodeVisionAVR максимально понятным: приводятся переводы всех меню и команд меню, диалоговых окон, а также различного рода предупреждений. Кроме того, для облегчения восприятия материала книга богато иллюстрирована и снабжена перекрёстными ссылками. Книга рассчитана на читателей, изучающих основы микроконтроллерной техники, и может быть полезна студентам вузов соответствующих специальностей. УДК 004.312.46 ББК 32.973.26-04-018.2 Все права защищены. Никакая часть этого издания не может быть воспроизведена в любой форме или любы- ми средствами, электронными или механическими, включая фотографирование, ксерокопирование или иные средства копирования или сохранения информации, без письменного разрешения издательства ISBN 978-5-94120-192-1 © Издательский дом «Додэка-ХХ1», 2008 © Лебедев М.Б., 2008
ОГЛАВЛЕНИЕ От автора...............................................................7 Предисловие.............................................................8 1. ИНСТАЛЛЯЦИЯ И ЗАПУСК CODEVISIONAVR..................................10 1.1. Инсталляция CodeVisionAVR..........................................10 1.2. Запуск CodeVisionAVR...............................................16 2. ИНТЕГРИРОВАННАЯ СРЕДА РАЗРАБОТКИ....................................18 2.1. Строка меню........................................................19 2.1.1. Меню File (Файл) ............................................20 2.1.2. Меню Edit (Правка) ..........................................30 2.1.3. Меню Project (Проект)........................................40 2.1.4. Меню Tools (Инструменты) ....................................44 2.1.5. Меню Settings (Настройки)....................................52 2.1.6. Меню Windows (Окна)..........................................64 2.1.7. Меню Help (Помощь) ..........................................67 2.2. Панель инструментов...............................................71 2.3. Строка состояния .................................................73 2.4. Область редактирования ...........................................75 2.5. Окно Navigator (Навигатор)........................................75 2.6. Окно Messages (Сообщения).........................................80 2.7. Экспорт лицензии на другой компьютер .............................80 3. РАБОТА В IDE CODEVISIONAVR.........................................83 3.1. Работа с файлами..................................................83 3.1.1. Редактирование файла.........................................84 3.2. Работа с проектами................................................86 3.2.1. Конфигурирование проекта.....................................87 3.2.2. Компиляция проекта..........................................104 3.2.3. Построение проекта..........................................106 3.2.4. Отладка программы ..........................................109 3.2.5. Запись программы в чип AVR .................................119 3.2.6. Закрытие проекта............................................147 3.2.7. Рекомендации................................................148
4 Оглавление 4. ОСНОВЫ ЯЗЫКА СИ.....................................................156 4.1. Препроцессор ......................................................158 4.1.1. Директива #include ...........................................158 4.1.2. Директивы #defme, #undef..................................... 159 4.1.3. Директивы #if, #ifdef, #ifndef, #else и #endif................162 4.1.4. Директива #line.............................................. 163 4.1.5. Директива #error............................................. 164 4.1.6. Директивы #asm и #endasm .................................... 164 4.1.7. Директивы #pragma ........................................... 165 4.1.8. Встроенные макросы........................................... 170 4.2. Зарезервированные ключевые слова.................................. 174 4.3. Идентификаторы ................................................... 174 4.4. Комментарии........................................................175 4.5. Константы .........................................................175 4.6. Переменные........................................................ 178 4.6.1. Массивы.......................................................183 4.6.2. Структуры ....................................................185 4.6.3. Объединения (смеси).......................................... 192 4.6.4. Перечисления ................................................ 197 4.6.5. Инициализация данных .........................................198 4.6.6. Файл распределения памяти глобальных переменных ............. 199 4.7. Типы данных........................................................200 4.8. Определение типов данных...........................................202 4.9. Преобразования типов...............................................203 4.10. Операнды и операции ..............................................206 4.10.1. Унарные операции.............................................206 4.10.2. Бинарные операции............................................209 4.10.3. Тернарные операции...........................................214 4.10.4. Приоритеты операций и порядок вычислений.....................215 4.11. Операторы ........................................................216 4.11.1. Оператор if-else.............................................217 4.11.2. Оператор switch .............................................217 4.11.3. ОператорГог..................................................219 4.11.4. Оператор while...............................................220 4.11.5. Оператор do-while............................................221 4.11.6. Оператор break...............................................222 4.11.7. Оператор continue............................................223 4.11.8. Оператор return..............................................224 4.11.9. Оператор goto ...............................................226 4.11.10. Оператор-выражение..........................................227 4.11.11. Пустой оператор.............................................227 4.11.12. Составной оператор..........................................228 4.12. Функции...........................................................229 4.13. Указатели ........................................................232 4.14. Доступ к регистрам ввода/вывода ..................................235 4.14.1. Побитовый доступ к регистрам ввода/вывода....................237
Оглавление 5 4.15. Доступ к EEPROM-памяти...............................................239 4.16. Использование прерываний ............................................241 4.17. Организация памяти SRAM .............................................243 4.18. Использование внешнего файла запуска ................................246 4.19. Включение в программу ассемблерного кода ............................248 4.19.1. Вызов ассемблерных функций из Си ...............................248 4.20. Создание библиотек...................................................250 4.21. Рекомендации ........................................................253 4.22. Ограничения .........................................................253 5. ИСПОЛЬЗОВАНИЕ БИБЛИОТЕЧНЫХ ФУНКЦИЙ.....................................254 5.1. Функции символьного типа .............................................255 5.2. Стандартные функции ввода/вывода языка Си.............................256 5.3. Стандартные библиотечные функции......................................266 5.4. Математические функции................................................269 5.5. Строковые функции.....................................................272 5.6. Макросы списков аргументов переменной длины ..........................275 5.7. Функции нелокальных переходов ........................................277 5.8. Функции двоично-десятичного преобразования ...........................279 5.9. Функции преобразования кода Грея......................................280 5.10. Функции доступа к памяти ............................................282 5.11. Функции протокола 1-Wire.............................................283 5.11.1. Функции температурного датчика DS 1820/DS18S20 от Dallas Semiconductor.............................................287 5.11.2. Функции EEPROM DS2430 от Dallas Semiconductor...................291 5.11.3. Функции EEPROM DS2433 от Dallas Semiconductor...................296 5.12. Функции SPI .........................................................301 5.13. Функции шины I2C.....................................................307 5.13.1. Функции температурного датчика LM75 от National Semiconductor ..313 5.13.2. Функции термометра/термостата DS1621 от Dallas Semiconductor.316 5.13.3. Функции часов реального времени PCF8563 от Philips..............319 5.13.4. Функции часов реального времени PCF8583 от Philips..............324 5.13.5. Функции часов реального времени DS1307 от Dallas Semiconductor .328 5.14. Функции часов реального времени DS1302 от Dallas Semiconductor ......332 5.15. LCD-функции .........................................................335 5.15.1. LCD-функции для дисплеев до 2x40 символов.......................342 5.15.2. LCD-функции для дисплеев с 4x40 символов........................353 5.15.3. LCD-функции для дисплеев, подключённых в режиме отображения 8-битовой памяти ..............................357 5.16. Функции управления питанием..........................................368 5.17. Функции задержки ....................................................370 6. АВТОМАТИЧЕСКИЙ ГЕНЕРАТОР ПРОГРАММ CODEWIZARDAVR.....................372 6.1. Строка меню ..........................................................373 6.1.1. Меню File (Файл) ................................................373 6.1.2. Меню Help (Помощь) ..............................................378
6 Оглавление 6.2. Закладки.............................................................379 6.2.1. Закладка Chip (Чип) ..........................................379 6.2.2. Закладка External SRAM (Внешнее SRAM) ........................380 6.2.3. Закладка Ports (Порты) .......................................382 6.2.4. Закладка External IRQ (Внешнее прерывание) ...................383 6.2.5. Закладка Timers (Таймеры).....................................384 6.2.6. Закладка UART или USART.......................................398 6.2.7. Закладка Analog Comparator (Аналоговый компаратор) ...........404 6.2.8. Закладка ADC..................................................406 6.2.9. Закладка SPI .................................................410 6.2.10. Закладка USI..................................................412 6.2.11. Закладка12С ..................................................414 6.2.12. Закладка 1 Wire ..........................................420 6.2.13. Закладка 2 Wire (I2C).........................................422 6.2.14. Закладка LCD Controller (Контроллер LCD) ATmegal69 .......... 423 6.2.15. Закладка LCD .................................................426 6.2.16. Закладка Bit-Banged ..........................................427 6.2.17. Закладка Project Information (Информация проекта).............428 6.3. Пример использования CodeWizardAVR...................................429 7. ПРИМЕРЫ ПРОЕКТОВ......................................................453 7.1. Проект «Led» ........................................................453 7.2. Проект «ADC8535».....................................................462 7.3. Проект «C asm» ......................................................468 7.4. Проект «Multfile» ...................................................471 7.5. Проект «EEPROM» .....................................................476 7.6. Проект «Lcddemo» ....................................................481 7.7. Проект «Lcdchar» ....................................................484 7.8. Проект «Keypad»......................................................488 7.9. Проект «Ds 1820» ....................................................513 7.10. Проект «Thermlcd» ..................................................525 7.11. Проект «Therm75» ...................................................532 7.12. Проект «SPI» .......................................................548 7.13. Проект «Мах1241» ...................................................557 7.14. Проект «AVR134» ....................................................570 Источники информации .....................................................590
ОТ АВТОРА Программа CodeVisionAVR рассматривается на примере версии 1.24.lx Standart. Автор постарался сделать описание программы CodeVisionAVR максимально понятным: приводятся переводы всех меню и команд меню, диало- говых окон, а также различного рода предупреждений. Кроме того, для облегче- ния восприятия материала книга богато иллюстрирована. В книге приводятся приёмы работы с той или иной частью программы. Рас- смотрены некоторые скрытые возможности CodeVisionAVR (например, директи- ва #pragma ruslcd). Автор постарался написать книгу так, чтобы её можно было читать с любого места. Для этого текст обильно снабжён перекрёстными ссылками. Например, разбирая примеры проектов в конце книги, можно в любой момент посмотреть более подробную информацию по соответствующей ссылке. Все примеры, приведённые в книге, опробованы автором. Автор надеется, что данная книга не только поможет начинающим русско- язычным пользователям в освоении микропроцессорной техники, но и опытные пользователи найдут в ней для себя много интересного. Все материалы, программы и схемы, приведённые в книге, представлены «как есть», без каких-либо гарантий соответствия фирменным описаниям. Автор не несёт никакой ответственности по материальному или другому виду ущерба, при- чинённому в результате использования информации, приведённой в настоящей книге. Все отзывы, замечания, предложения, замеченные ошибки и опечатки просьба отправлять по электронному адресу автора: mihleb@pochta.ru с пометкой в графе «Тема» — книга. Автор выражает большую благодарность В.В. Кошкину за консультации и большую помощь, оказанную при написании этой книги. С уважением, М.Б. Лебедев.
ПРЕДИСЛОВИЕ CodeVisionAVR — это кросс-компилятор Си, Интегрированная среда разра- ботки (IDE — Integrated Development Environment) и Автоматический генератор программ (CodeWizardAVR), разработанные для семейства AVR-микроконтролле- ров фирмы Atmel. Программа является 32-битовым приложением, которое работает под опера- ционными системами Windows 95, 98, NT 4, 2000 и ХР. CodeVisionAVR обеспечивает выполнение почти всех элементов языка Си, ко- торые разрешены архитектурой AVR, с некоторыми добавленными характеристи- ками, которые реализуют преимущество специфики архитектуры AVR. Компилятор предназначен для использования вместе с отладчиком AVR Studio от Atmel версии 4.06 или более поздней. AVR Studio можно бесплатно загру- зить с веб-сайта: www.atmel.com. IDE имеет программное обеспечение встроенного внутрисхемного програм- матора чипов AVR, который позволяет автоматически передавать программы в микроконтроллерный чип после успешной компиляции. Программное обеспече- ние внутрисхемного программатора может работать совместно с Atmel STK500/AVRISP/AVRProg (прикладное описание AVR910 от Atmel), Kanda Systems STK200+/300, Dontronics DT006, Vogel Elektronik VTEC-ISP, Futurlec JRAVR и платой разработчика MicroTronics ATCPU/Mega2000. Для отладки разрабатываемых систем, которые применяют последовательную связь, IDE имеет встроенный терминал. Кроме стандартных библиотек Си, компилятор Си CodeVisionAVR имеет биб- лиотеки для: • алфавитно-цифровых LCD-модулей; • шины 12С от Philips; • температурного датчика LM75 от National Semiconductor; • часов реального времени PCF8563, PCF8583 от Philips и DS1302, DS1307 от Dallas Semiconductor; • протокола 1 -Wire от Dallas Semiconductor; • температурного датчика DS 1820/DS18S20 от Dallas Semiconductor; • термометра/термостата DS1621 от Dallas Semiconductor; • EEPROM DS2430 и DS2433 от Dallas Semiconductor; • SPI;
Предисловие 9 • управления питанием; • задержек; • преобразования кода Грея. CodeVisionAVR также содержит Автоматический генератор программ — CodeWizardAVR, который позволяет написать за несколько минут весь код, необ- ходимый для выполнения следующих функций: • установка доступа к внешней памяти; • идентификация источника сброса чипа; • инициализация порта ввода/вывода; • инициализация внешних прерываний; • инициализация таймеров/счётчиков; • инициализация сторожевого таймера; • инициализация UART и прерываний, управляющих буфером последова- тельной связи; • инициализация аналогового компаратора; • инициализация АЦП; • инициализация интерфейса SPI; • инициализация шины 12С, температурного датчика LM75, термометра/тер- мостата DS1621 и часов реального времени PCF8563, PCF8583, DS1302, DS1307; • инициализация шины 1-Wire и температурного датчика DS1820/DS18S20; • инициализация LCD-модуля.
ГЛАВА I ИНСТАЛЛЯЦИЯ И ЗАПУСК CodeVisionAVR 1.1. Инсталляция CodeVisionAVR Для инсталляции с сайта разработчика http://www.hpinfotech.ro следует загру- зить установочный файл программы CodeVisionAVR. Если CodeVisionAVR устанавливается под Windows NT4, 2000 или ХР, то тре- буется иметь права администратора. После установки программы в первый раз её следует запустить от имени администратора. После этого можно запускать CodeVisionAVR от имени любого другого пользователя. Для инсталляции следует запустить файл CodeVisionAVR 1.24.1x.exe, дважды щёлкнув по нему левой кнопкой мыши в проводнике, или запустить его из ко- мандной строки DOS. При этом появится окно Welcome (Добро пожаловать), по- казанное на Рис. 1.1. Скриншоты могут немного отличаться. Welcome Welcome to the CodeVisionAVR C Compiler Setup program. This program will install CodeVisionAVR C Compiler on your computer. It is strongly recommended that you shut down all Windows applications before running this installation. Click Cancel to exit Setup and close any applications you have running. Click Next to continue the installation. WARNING: This program is protected by copyright law and international treaties. Unauthorized reproduction or distribution of this program, or any portion of it, may result in severe civil and criminal penalties, and will be prosecuted to the maximum extent possible under law. Puc. 1.1. Приглашение в программу установки компилятора Си CodeVisionAVR. B.-ack |[/ Next j| Cancel
1.1. Инсталляция Code Vision AVR 11 Если в процессе инсталляции щёлкнуть по кнопке Cancel (Отменить) в любом из появляющихся далее окон, то инсталляцию можно в любой момент прекратить. Что- бы продолжить инсталляцию, следует щёлкнуть по кнопке Next (Далее). Появится окно Software License Agreement (Лицензионное соглашение программы) (Рис. 1.2). Software License Agreement Please read the following License Agreement for CodeVisionAVR C Compiler. Press the PAGE DOWN key. or use the scrollbars to read the rest of the agreement. SOFTWARE LICENCE The use of CodeVisionAVR indicates your understanding and acceptance of the following terms and conditions. This license shall supersede any verbal or prior verbal or written, statement or agreement to the contrary. If you do not understand or accept these terms, or your local regulations prohibit "after sale" license agreements or limited disclaimers, you must cease and desist using this product immediately. This product is (C) Copyright 1998-2004 Pavel Haiduc and HP InfoTech s.r.l., all rights reserved. International copyright laws, international treaties and all other applicable national or international laws Drotect this oroduct. Do you accept all the terms of the preceding License Agreement? If you choose No. the installation will terminate. T о install CodeVisionAVR C Compiler, you must accept this agreement. Back I Yes I No Рис. 1.2. Лицензионное соглашение. Следует прочесть лицензионное соглашение и в случае его непринятия щёлк- нуть по кнопке No (Нет), чтобы прекратить инсталляцию. В случае принятия ли- цензионного соглашения следует щёлкнуть по кнопке Yes (Да). При этом появит- ся окно Product Information (Информация о продукте) (Рис. 1.3). Product Information Information: Welcome to the CodeVisionAVR C Compiler and the CodeWizardAVR Automatic Program Generator The compiler is designed to be used along with the Atmel AVR Studio debugger V4.07 or later. In case you don’t have AVR Studio, then you must download it from the ATMEL web site: www.atmel.com If you will use the Compiler under Windows NT4, 2000 or XP. you must first run it with Administrator privileges. On subsequent runs you may also have Power User privileges. Also if the installation directory is different from the default c:\cvavr. then it must not contain embedded spaces. The Compiler is supplied with the following example Back | N[ext Cancel Рис. 1.3. Информация о продукте.
12 Глава 1. Инсталляция и запуск CodeVisionAVR Эта информация будет установлена как файл readme.txt в директории про- граммы. После прочтения информации следует щёлкнуть по кнопке Next (Да- лее). Появится окно Choose Destination Directory (Выбор директории для установ- ки) (Рис. 1.4). Рис. 1.4. Назначение директории для установки программы. В этом окне следует выбрать директорию, в которую будет установлена про- грамма CodeVisionAVR. По умолчанию CodeVisionAVR будет установлена в ката- лог: C:\cvavr. Если требуется изменить местоположение и имя каталога, следует щёлкнуть по кнопке Browse (Обзор). Появится окно Select Directory (Выбор директории) (Рис. 1.5). Рис. 1.5. Выбор директории.
7.7. Инсталляция Code Vision A VR 13 В этом окне следует выбрать каталог для установки или ввести непосредственно путь к нему. Например: D:\AVRTools\CodeVision (Рис. 1.6). Следует избегать того, чтобы в названиях каталогов был знак «.». Причи- на этого будет объяснена ниже (см. Команда File -> Save As... (Файл Сохранить как...)). Рис. 1.6. Указание пути для установки программы. После выбора каталога для установки следует щёлкнуть по кнопке ОК. Если данной директории не существует, то будет запрошено подтверждение на созда- ние новой (Рис. 1.7). Рис. 1.7. Запрос подтверждения на создание новой директории. В случае согласия следует щёлкнуть по кнопке Yes. Теперь путь для установки будет изменён (Рис. 1.8). После этого следует щёлкнуть по кнопке Next. Теперь в появившемся окне Select Program Folder (Выбор программной папки) следует вы- брать группу программ, в которую должна быть помещена CodeVisionAVR (Рис. 1.9).
14 Глава 1. Инсталляция и запуск Code VisionA VR Рис. 1.8. Выбор группы программ. Select Program Folder Setup will add program icons to the Program Folder listed below. You may type a new folder name or select an existing one from the Existing Folders list. Click Next to continue. Program Folder: | CodeVisionAVR Existing Folder: ACD Systems Ol AVR Tools CodeVisionAVR EPSON FinePrint FinePrint pdfFactory Pro Light Alloy System Mechanic 4 Professional Winamp WinAVR HJ Back Next I Cancel Рис. 1.9. Назначение новой директории для установки программы. По умолчанию будет создана новая группа программ с именем CodeVisionAVR. Если требуется, можно поместить CodeVisionAVR в другую груп- пу программ, например так, как показано на Рис. 1.10. После выбора группы следует щёлкнуть по кнопке Next, чтобы продолжить. В появившемся окне Start Copying Files (Начало копирования файлов) будет пока- зан результат ваших действий (Рис. 1.11).
7.7. Инсталляция Code VisionA VR t 15 Puc. 1.10. Выбор группы программ. Start Copying Files Setup has enough information to start copying the program files. If you want to review or change any settings, click Back. If you are happy with your selections, click Next to start copying files. Current Settings: Setup Type: Full T arget Folder: D:\AVRT oolsKCodeVision Program Group: AVR T ools\CodeVisionAVR gack |i Next I Cancel Puc. 1.11. Текущие настройки для инсталляции программы. Можно вернуться и изменить установочные параметры, щёлкнув по кнопке Back (Назад). Чтобы завершить инсталляцию CodeVisionAVR, следует щёлкнуть по кнопке Next. После этого появится окно Setup Complete (Завершение установ- ки) (Рис. 1.12). Если требуется сразу после инсталляции запустить программу, то следует поставить «галочку» в окошке Run CodeVisionAVR С Compiler now (Запус- тить компилятор Си CodeVisionAVR сейчас), как показано на Рис. 1.12,
16 Глава 1. Инсталляция и запуск CodeVisionAVR Рис. 1.12. Завершение инсталляции программы. В заключение следует щёлкнуть по кнопке Finish (Конец). На рабочем столе появится значок программы CodeVisionAVR. В меню Пуск в разделе Все программы в группе AVR Tools появится подгруппа CodeVisionAVR. В директории, куда установлена программа CodeVisionAVR, подкаталог с име- нем Examples (Примеры) содержит примеры проектов CodeVisionAVR; подката- лог с именем lib (библиотека) содержит библиотечные файлы. 1.2. Запуск CodeVisionAVR При первом запуске появится окно, призывающее зарегистрировать программу (Рис. 1.13). CodeVisionAVR License Your Serial Number is: 2288-1364-4FB2-CA90 and it has been copied to the clipboard. T о purchase a license, please send this Serial Number to HP InfoTech s.r.l. at: of fice@hpinfotech. ro If you allready purchased the program, then you must Import the license from a valid CodeVisionAVR installation Import J| X Cancel Puc. 1.13. Регистрация программы CodeVisionAVR.
/. 2. Запуск Code VisionA VR 17 Следует скопировать серийный номер (выделен красным цветом) в буфер об- мена. Чтобы приобрести лицензию, следует послать этот серийный номер по электронному адресу: office@hpinfotech.ro. Если программа уже куплена, то следу- ет импортировать лицензию. Для этого следует щёлкнуть по кнопке Import (Им- порт). Появится окно, в котором следует указать путь к файлу лицензии (Рис. 1.14). Рис. 1.14. Импорт лицензионного файла. Подробнее о приобретении лицензии см. файл License.txt, находящийся в ди- ректории программы. После успешной регистрации появится окно программы (Рис. 1.15). Рис. 1.15. Окно программы CodeVisionAVR с загруженным проектом. Если программа уже запускалась ранее, то будет загружен файл, который от- крывался последним. Если это первый запуск программы, то окно редактирования будет пустым (Рис. 2.1).
ГЛАВА ИНТЕГРИРОВАННАЯ СРЕДА РАЗРАБОТКИ Окно IDE CodeVisionAVR (Рис. 2.1) содержит много стандартных элементов Windows, включая строку заголовка (1), где выводятся названия программы и от- крытого в данный момент файла проекта; кнопку минимизации (2), позволяющую временно свернуть окно программы; кнопку разворачивания (3), позволяющую развернуть окно программы на весь экран и/или вернуть в первоначальное состо- яние; кнопку закрытия (4), позволяющую закрыть окно программы (выйти из программы); строку меню (5), позволяющую выбирать команды из различных ме- ню; панель инструментов (6), состоящую из кнопок, с помощью которых легко за- давать разнообразные команды, и строку состояния (7) для вывода информации о текущем состоянии программы. Окно программы разделено на три области: об- ласть редактирования (8), предназначенная для просмотра и редактирования фай- лов проекта; окно Navigator (9), с помощью которого осуществляется удобная на- вигация по всему проекту, и окно Messages (10), в котором выводятся различные сообщения об ошибках и предупреждения. Рис. 2.1. Окно IDE CodeVisionAVR.
2.1. Строка меню 19 Строка заголовка, а также кнопки минимизации, разворачивания и закрытия аналогичны стандартным окнам Windows-программ и здесь подробно рассматри- ваться не будут. 2.1. Строка меню В этой строке выведены названия меню, которые предоставляют доступ к их командам (Рис. 2.2). File Edit Project Tools Settings Windows Help Puc. 2.2. Строка меню. При работе с программой следует использовать команды, которые сообщают CodeVisionAVR, что именно требуется сделать. Команды CodeVisionAVR можно найти в меню, а многие из них можно вызвать с помощью кнопок панели инстру- ментов. Выбор способа доступа зависит только от пользователя. Для того чтобы выбрать команду меню, следует: • открыть меню, щёлкнув на его названии в строке меню; • в открытом меню щёлкнуть по названию требуемой команды. В этой книге для обозначения команд меню будут использованы сокраще- ния. Например, если написано File -> Open (Файл Открыть), значит, следует открыть меню File (Файл), а затем выбрать команду Open (От- крыть). С помощью нескольких элементов, используемых в меню, CodeVisionAVR обеспечивает пользователя дополнительной информацией. В Табл. 2.1 даётся описание этих элементов. Чтобы выполнить некоторые команды, не используя меню вообще, следует использовать комбинацию клавиш. Эти клавиши записываются в меню справа от соответствующей команды. Таблица 2.1. Элементы меню Элемент меню Назначение Кнопка Если для команды меню предусмотрена соответствующая кнопка панели инструментов, она выводится рядом с командой Многоточие Показывает, что при выборе этой команды меню появится диалоговое окно Стрелка подменю Показывает, что при выборе этой команды появится другое меню (оно называется подменю) Комбинация клавиш Указывает клавиши, с помощью которых можно вызвать команду меню, используя клавиатуру Многие команды CodeVisionAVR используют диалоговые окна. CodeVisionAVR использует эти окна для получения дополнительной информа- ции, необходимой для выполнения команды. Все диалоговые окна различны, но в каждом из них есть одни и те же основные элементы.
20 Глава 2, Интегрированная среда разработки 2.1.1. Меню File (Файл) В состав меню File (Файл) входит несколько команд. В Табл. 2.2 даётся крат- кое описание этих команд. Таблица 2.2. Команды меню File (Файл) Команда Назначение New (Новый) Позволяет создать новый исходный файл или новый проект Open (Открыть) Позволяет открыть ранее созданный файл или проект Reopen (Открыть заново) Позволяет открыть файл или проект, который ранее уже открывался Save (Сохранить) Сохраняет файл, находящийся в активном окне, под тем же именем Save As (Сохранить как) Позволяет сохранить файл, находящийся в активном окне, под новым именем Save АН (Сохранить всё) Сохраняет все открытые файлы и открытый проект под теми же именами Close (Закрыть) Закрывает файл, находящийся в активном окне. При этом проект остаётся открытым Close Project (Закрыть проект) Закрывает проект Convert to Library (Преобразовать в библиотеку) Позволяет преобразовать файл с расширением .с, находящийся в активном окне, в библиотечный файл с расширением .lib Page Setup (Параметры страницы) Позволяет настроить параметры страницы и принтера для вывода на печать Print (Печать) Печатает файл, находящийся в активном окне Exit (Выход) Осуществляет выход из программы Команда File -> New (Файл -> Новый) Эта команда позволяет создать новый исходный файл или новый проект. Кнопка на панели инструментов Р]. После выбора этой команды появится диалоговое окно Create New File (Со- здать новый файл), показанное на Рис. 2.3. Рис. 2.3. Диалоговое окно Create New File (Создать новый файл). В этом окне следует выбрать тип создаваемого файла (File ТУре): Source (Ис- ходник) или Project (Проект). После выбора следует щёлкнуть по кнопке ОК. Если выбрать Source (Исходник), то в области редактирования появится но- вое окно редактора для вновь создаваемого файла (Рис. 2.4). В этом окне можно писать программу пользователя. Курсор устанавливается в новом окне.
2.1. Строка меню 21 Новый файл имеет имя untitled.c. Его название также появляется в окне Navigator (Навигатор). Можно сохранить этот файл под новым именем (см. Ко- манда File Save As... (Файл -> Сохранить как...)). Обратите внимание, что, пока файл не включён в проект, его нельзя от- компилировать. т. е. для отдельного файла IDE CodeVisionAVR представ- ляет собой не более чем текстовый редактор с подсветкой синтаксиса. Рис. 2.4. Новое окно редактора для файла untitled.c. Если в диалоговом окне Create New File (Создать новый файл) выбрать Project (Проект), как показано на Рис. 2.5, то появится диалоговое окно Confirm (Под- тверждение), в котором будет спрошено, следует ли для создания нового проекта использовать CodeWizardAVR (Автоматический генератор программ) (Рис. 2.6). Puc. 2.5. Создание нового проекта. Confiirn 9 You are about to create a new project. • 7 Do you want to use the CodeWizardAVR? IlJEEjI №> I Рис. 2.6. Диалоговое окно Confirm. Если выбрать Yes, то будет запущен CodeWizardAVR (см. Автоматический гене- ратор программ CodeWizardAVR). Если выбрать No, то откроется диалоговое окно Create New Project (Создание нового проекта) (Рис. 2.7), где следует определить файловое имя нового проекта и его местоположение.
22 Глава 2. Интегрированная среда разработки Рис. 2.7. Диалоговое окно Create New Project (Создание нового проекта). Файл проекта будет иметь расширение .pij. После того как имя файла проекта и его местоположение определены, следует щёлкнуть по кнопке Сохранить. После этого проект будет создан, и будет предло- жено его сконфигурировать, как показано на Рис. 2.8. Рис. 2.8. Диалоговое окно Configure Project (Конфигурация проекта).
2.1. Строка меню 23 Если щёлкнуть по кнопке ОК, то появится окно нового пустого проекта (Рис. 2.9). При этом текущий проект будет закрыт! Рис. 2.9. Окно нового пустого проекта. Конфигурацию проекта можно изменить в любой момент (см. Команда Project Configure (Проект —> Конфигурировать)) Подробнее о конфигурировании проекта будет рассказано ниже (см. Конфигу- рирование проектов). Одновременно можно работать только с одним проектом. При открытии нового проекта текущий проект закроется автоматически. Команда File Open (Файл -> Открыть) Эта команда позволяет открыть ранее созданный файл или проект. Кнопка на панели инструментов I После выбора этой команды появится диалоговое окно Open (Открыть), пока- занное на Рис. 2.10. Рис. 2.10. Диалоговое окно Open (Открыть). Выбор расширения файла.
24 Глава 2, Интегрированная среда разработки После этого в области редактирования появится новое окно для открытого файла (Рис. 2.11). Его название также появится в окне Navigator (Навигатор). Этот файл можно сохранить под новым именем (см. Команда File -> Save As... (Файл Сохранить как...)). Курсор устанавливается в новом окне. Рис. 2.11. Новое окно редактора для файла led.asm. Обратите внимание, что если выбрать файл с расширением .prj, то теку- щий проект будет закрыт!!! Команда File Reopen (Файл -> Открыть заново) Эта команда позволяет открыть файл или проект, который ранее уже откры- вался. CodeVisionAVR - led.prj File Edit Project Tools Settings Windows Help Reopen d: \avrtools\codevision\examples\proba\proba. prj d: \avrtools\codevision\examples\c_asm\c_asm. prj c:\cvavr\led\led.pri [b New Open O’I Ы Savt ct'lf-s Save As... Save All Close Close Project И Convert to library 0 Page Setup # Print Qrl+P c: \cvavr \examples\multfU$multf ile. prj d: \avrtools\codevision\examples\multf ile\multf ile. prj d: \avrtools\code vision\examples\ds 1820\ds 1820. prj d: \a vrtools\codevision\inc\ 1 wire. h d: \avrtools\codevision\examples\adc8535\adc8535. c d: \avrtools\codevision\examples\led\untitled. c d: \avrtools\codevision\examples\c_asm\c_asm. c c:\cvavr\led\led.c c: \c vavr \examples\multf ile\mainf ile. c Puc. 2.12. Выбор команды File -> Reopen (Файл -> Открыть заново).
2,1. Строка меню 25 После выбора этой команды появится выпадающее окно с историей открыва- емых ранее файлов и проектов (Рис. 2.12). Оно разделено на две части: в верхней части перечислены файлы проектов, в нижней — все прочие файлы. Щёлкнув по выбранному файлу, его можно открыть заново. Причём если вы- брать файл проекта, то текущий проект будет закрыт. Если выбрать любой другой файл, то он будет открыт в области редактирова- ния без закрытия текущего проекта. Название открытого заново файла появится в окне Navigator (Навигатор) (Рис. 2.13). Рис. 2.13. Открытый заново файл c_asm.c. Команда File -> Save (Файл -> Сохранить) Эта команда сохраняет файл из активного окна под тем же именем. Кнопка на панели инструментов (Д]. После внесения в файл каких-либо изменений команда File -> Save (Файл Сохранить) становится активной. Комбинация «горячих» клавиш — <Ctrl+S>. Команда File Save As... (Файл -> Сохранить как...) Эта команда позволяет сохранить файл из активного окна под новым именем. После выбора этой команды (Рис. 2.14) появится диалоговое окно Save ... As (Со- хранить ... как), показанное на Рис. 2.15. В этом окне следует ввести новое имя файла (не забудьте проверить его расширение) и щёлкнуть по кнопке Сохранить (Рис. 3.25). При этом файл будет сохранён под новым именем. Таким образом, эта команда позволяет сохранять различные версии одного и того же файла.
26 Глава 2. Интегрированная среда разработки Обратите внимание, что файл со старым именем не будет удален и оста- нется на диске. Рис. 2.14. Выбор команды File -» Save As... (Файл -> Сохранить как...). Рис. 2.15. Диалоговое окно Save ... As (Сохранить ... как). Если попытаться сохранить файл в директории, в названии которой содер- жится знак точки «.», например D:\AVRTools\CodeVisionl.24.8, то CodeVisionAVR выведет окно Error с сообщением об ошибке (Рис. 2.16). Рис. 2.16. Окно Error (Ошибка).
2.1. Строка меню 27 В этом окне говорится, что имя файла может содержать только один символ «.». Поэтому при установке программы следует избегать в названиях директорий символа точки. Команда File -> Save АП (Файл -> Сохранить всё) Эта команда сохраняет все открытые файлы и открытый проект под теми же именами. Этой командой удобно пользоваться, если во время работы над проектом бы- ли внесены изменения сразу в несколько файлов. Команда File -> Close (Файл -> Закрыть) Эта команда закрывает файл в активном окне редактора. Если файл был изменён и после этого не сохранялся, то появится диалоговое окно Confirm (Подтверждение), в котором будет предложено сохранить эти изме- нения. Если щёлкнуть по кнопке Yes (Да), то изменения будут сохранены и файл бу- дет закрыт. Если щёлкнуть по кнопке No (Нет), то файл будет закрыт без сохране- ния изменений. Если щёлкнуть по кнопке Cancel (Отменить), то закрытие файла будет отменено. Обратите внимание, что проект останется открытым, даже если за- крыть все файлы, входящие в проект. Этой командой также нельзя за- крыть файл Note (Примечание). Команда File -> Close Project (Файл -> Закрыть проект) Эта команда закрывает весь проект. Команда File -> Convert to Library (Файл -> Преобразовать в библиотеку) Эта команда позволяет преобразовать файл с расширением .с в активном окне редактора в библиотечный файл с расширением .lib. Эта команда используется для создания собственных библиотек пользователя (см. Создание библиотек). Команда File -> Page Setup (Файл -> Параметры страницы) Эта команда позволяет настроить параметры страницы и принтера для вывода на печать. После выбора этой команды появится диалоговое окно Page Setup (Парамет- ры страницы), показанное на Рис. 2.17. В этом окне можно выбрать Printing Options (Опции печати) и настроить Margins (Поля).
28 Глава 2. Интегрированная среда разработки Рис. 2.17. Диалоговое окно Page Setup (Параметры страницы). В Табл. 2.3 даётся краткое описание Printing Options (Опций печати). Если выбрать все опции, то напечатанная страница будет выглядеть примерно так, как показано на Рис. 2.18. Таблица 2.3. Назначение Printing Options (Опций печати) Опция Назначение Page Header (Заголовок страницы) Если выбрать эту опцию, то при печати вверху каждой страницы будет выводиться заголовок, состоящий из полного пути к файлу и текущей даты Page Numbers (Номера страниц) Если выбрать эту опцию, то при печати внизу каждой страницы будет выводиться номер страницы Line Numbers (Номера строк) Если выбрать эту опцию, то при печати в начале каждой строки будет выводиться ее номер Highlight Syntax (Подсветка синтаксиса) Если выбрать эту опцию, то при печати текст файла будет выводиться с подсветкой синтаксиса В Табл. 2.4 даётся краткое описание параметров опции Margins (Поля) диало- гового окна Page Setup (Параметры страницы) (см. Рис. 2.18). Таблица 2.4. Параметры опции Margins (Поля) Параметр Назначение Left (Слева) Задает размер левого поля страницы Right (Справа) Задает размер правого поля страницы Тор (Верх) Задает размер верхнего поля страницы Bottom (Низ) Задает размер нижнего поля страницы Units (Единицы измерения) Позволяет выбрать единицы измерения размера поля: мм (mm) или дюймы (inch)
2.1. Строка меню 29 Рис. 2.18. Вид напечатанной страницы при выборе всех опций печати. В нижней части диалогового окна Page Setup (Параметры страницы) располо- жена кнопка Printer (Принтер). Если щёлкнуть по этой кнопке, то появится стан- дартное диалоговое окно Windows Настройка печати (Рис. 2.19). В этом окне стандартными методами Windows можно настроить размеры, по- дачу и ориентацию бумаги, а также установить требуемые свойства принтера. Кроме того, в этом окне для печати можно указать сетевой принтер (кнопка Сеть...),
30 Глава 2. Интегрированная среда разработки Изменения параметров страницы можно сохранить, щёлкнув в диалоговом окне Page Setup (Параметры страницы) по кнопке ОК, или отменить, щёлкнув по кнопке Cancel (Отменить). Рис. 2.19. Диалоговое окно Настройка печати. Команда File -> Print (Файл -> Печать) Эта команда выводит на печать файл, находящийся в активном окне редактора. Кнопка на панели инструментов . Комбинация «горячих» клавиш — <Ctrl+P>. После выбора этой команды сразу начнётся печать с параметрами, установ- ленными командой Page Setup (Параметры страницы). Команда File -> Exit (Файл -> Выход) Эта команда осуществляет выход из программы. Если перед выходом из программы изменения в проекте не были сохранены, то после выбора команды File —> Exit (Файл Выход) появится диалоговое окно Confirm (Подтверждение), в котором будет предложено сохранить эти изменения. 2.1.2. Меню Edit (Правка) В состав меню Edit (Правка) входит несколько команд. В Табл. 2.5 даётся краткое описание этих команд. Таблица 2.5. Команды меню Edit (Правка) Команда Действие Undo (Отменить изменение) Отменяет изменения в редактируемом файле Redo (Восстановить изменение) Возвращает отмененные изменения в редактируемом файле
2.1. Строка меню 31 (продолжение) Команда Действие Сиг (Вырезать) Копирует выделенный фрагмент в буфер обмена и удаляет его из редактируемого файла Сору (Копировать) Копирует выделенный фрагмент в буфер обмена Paste (Вставить) Вставляет содержимое буфера обмена в редактируемый файл в текущую позицию курсора Delete (Удалить) Удаляет выделенный фрагмент из редактируемого файла Select АН (Выбрать все) Выделяет весь текст в окне редактора Print Selection (Печатать выбранное) Печатает выделенный фрагмент редактируемого файла Indent Block (Увеличить отступ) Увеличивает отступ выделенного фрагмента редактируемого файла Unindent Block (Уменьшить отступ) Уменьшает отступ выделенного фрагмента редактируемого файла Find (Найти) Осуществляет поиск фрагмента в редактируемом файле Find Next (Найти далее) Осуществляет дальнейший поиск фрагмента в редактируемом файле Find in Files (Найти в файлах) Осуществляет поиск фрагмента во всех файлах, открытых в области редактирования Replace (Заменить) Заменяет один фрагмент в редактируемом файле на другой Toggle Bookmark (Переключить закладку) Устанавливает/удаляет закладку Jump to Bookmark (Перейти к закладке) Осуществляет переход к соответствующей закладке Goto Line (Перейти к строке) Осуществляет переход к соответствующей строке файла Match Braces (Выделить в скобках) Выделяет текст, заключённый в скобках Команда Edit Undo (Правка Отменить изменение) Эта команда позволяет отменить изменения, сделанные в файле в процессе редактирования. . Кнопка на панели инструментов . Комбинация «горячих» клавиш — <Ctrl+Z>. Пока в файл, который находится в активном окне, не внесено никаких изме- нений, эта команда (и кнопка на панели инструментов) не активна. После выбора этой команды будет отменено последнее изменение в файле. Если требуется отменить предыдущее изменение, следует выбрать эту команду ещё раз и т. д. Команда Edit Redo (Правка Восстановить изменение) Эта команда позволяет восстановить изменения, отменённые командой Edit -» Undo (Правка Отменить изменение). Кнопка на панели инструментов £* . Комбинация «горячих» клавиш — <Shift+Ctrl+Z>. Пока не отменено ни одно изменение, команда Edit Redo (Правка -> Восста- новить изменение) не активна.
32 Глава 2. Интегрированная среда разработки Команда Edit Cut (Правка Вырезать) Эта команда копирует выделенный фрагмент текста в буфер обмена и удаляет его из редактируемого файла. j Кнопка на панели инструментов & I. Комбинация «горячих» клавиш — <Ctrl+X>. Пока в файле, который находится в активном окне, нет выделенных фрагмен- тов текста, эта команда (и кнопка на панели инструментов) не активна. После выбора этой команды выделенный фрагмент текста будет вырезан из файла и помещён в буфер обмена, откуда его можно вставлять в редактируемый файл, в другие файлы проекта, а также в другие текстовые редакторы. Команда Edit -> Сору (Правка Копировать) Эта команда копирует выделенный фрагмент текста в буфер обмена. Кнопка на панели инструментов Ч=)|. Комбинация «горячих» клавиш — <Ctrl+C>. Пока в файле, который находится в активном окне, нет выделенных фрагмен- тов текста, эта команда (и кнопка на панели инструментов) не активна. После выбора этой команды выделенный фрагмент текста будет помещён в буфер обмена, откуда его можно вставлять в редактируемый файл, в другие файлы проекта, а также в другие текстовые редакторы. Команда Edit Paste (Правка Вставить) Эта команда вставляет содержимое буфера обмена в редактируемый файл. Кнопка на панели инструментов . Комбинация «горячих» клавиш — <Ctrl+V>. Пока буфер обмена пуст, эта команда (и кнопка на панели инструментов) не активна. После выбора этой команды текст, находящийся в буфере обмена, будет вставлен в текущую позицию курсора. Команда Edit Delete (Правка Удалить) Эта команда удаляет выделенный фрагмент текста из редактируемого файла. Комбинация «горячих» клавиш — <Ctrl+Delete>. Пока в файле, который находится в активном окне, нет выделенных фрагмен- тов текста, эта команда не активна. Команда Edit -> Select АН (Правка Выбрать всё) Эта команда выделяет весь текст в активном окне редактора. Комбинация «горячих» клавиш — <Ctrl+A>. После выбора этой команды будет выделен весь текст в активном окне редактора.
2.1. Строка меню 33 Команда Edit -> Print Selection (Правка -> Печатать выбранное) Эта команда печатает выделенный фрагмент редактируемого файла. После выбора этой команды сразу, безо всякого предупреждения, начинается печать выделенного фрагмента текста. Команда Edit -> Indent Block (Правка -> Увеличить отступ) Команда увеличивает отступ выделенного фрагмента редактируемого файла. Комбинация «горячих» клавиш — <Ctrl+I>. Пока в файле, который находится в активном окне, нет выделенных фрагмен- тов текста, эта команда не активна. После выбора этой команды отступ выделенного фрагмента текста будет уве- личен на значение табуляции (Tab), которое задано в настройках редактора (см. Команда Settings Editor (Настройки Редактор)). С помощью этой команды текст программы можно сделать более удобочитае- мым (Рис. 2.20). Рис. 2.20. С помощью отступов текст программы сделан более удобочитаемым. Команда Edit Unindent Block (Правка Уменьшить отступ) Эта команда уменьшает отступ выделенного фрагмента редактируемого файла. Комбинация «горячих» клавиш — <Ctrl+U>. Пока в файле, который находится в активном окне, нет выделенных фрагмен- тов текста, эта команда не активна.
34 Глава 2. Интегрированная среда разработки После выбора этой команды отступ выделенного фрагмента текста будет уменьшен на значение табуляции (Tab), которое задано в настройках редактора (см. Команда Settings -» Editor (Настройки -> Редактор)). Команда Edit -э Find (Правка -> Найти) Команда позволяет осуществить поиск фрагмента текста в редактируемом файле. Кнопка на панели инструментов ,,ft| . Комбинация «горячих» клавиш — <Ctrl+F>. После выбора этой команды появится диалоговое окно Найти, показанное на Рис. 2.21. Для осуществления поиска следует ввести искомый фрагмент текста и кликнуть по кнопке Найти далее. Также можно осуществить поиск слова целиком или с учётом регистра. Для этого необходимо кликнуть (щёлкнуть) по соответс- твующим окошкам. Поиск можно осуществлять как вверх по тексту, так и вниз. Направление по- иска определяется в окошке Направление. Рис. 2.21. Диалоговое окно Найти. Если сначала выделить в тексте какой-то фрагмент, а затем выбрать команду Edit -> Find (Правка Найти), то в поле «Что:» окажется вы- деленный фрагмент текста. Команда Edit -> Find Next (Правка -> Найти Далее) Эта команда позволяет осуществить дальнейший поиск фрагмента текста в редактируемом файле. Комбинация «горячих» клавиш — <F3>. После выбора этой команды будет осуществлён дальнейший поиск фрагмента текста, который был задан командой Edit -> Find (Правка -> Найти). Намного удобнее осуществлять дальнейший поиск нажатием клавиши F3. Команда Edit -> Find in Files (Правка -> Найти в файлах) Эта команда позволяет осуществить поиск фрагмента текста во всех файлах, находящихся в области редактирования. После выбора этой команды появится диалоговое окно Find in Files (Найти в файлах), показанное на Рис. 2.22. Для осуществления поиска следует в поле Find
2.1. Строка меню 35 what: (Что искать:) ввести искомый фрагмент текста и щёлкнуть по кнопке Find (Искать). Для отмены поиска следует щёлкнуть по кнопке Cancel (Отмена). Рис. 2.22. Диалоговое окно Find in Files (Найти в файлах). Если поставить галочки в окошках Match whole word only (Только слово цели- ком) и Match case (С учетом регистра), щёлкнув по этим окошкам, то будет осу- ществлен поиск только слова целиком и именно в этом регистре. Снять галочки можно, щёлкнув по этим окошкам ещё раз. После окончания поиска в окне Messages (Сообщения) появится закладка ок- на Find in Files (Найдено в файлах). В этом окне будет выведен список всех строк текста, удовлетворяющих условиям поиска, причём будет выведен полный путь к файлу, позиция курсора, с которой начинается искомый фрагмент, а также вся строка текста с искомым фрагментом. Если щёлкнуть по какой-либо строке в ок- не Find in Files (Найдено в файлах), то в соответствующем файле искомый фраг- мент текста будет выделен (Рис. 2.23). Рис. 2.23. Результаты поиска в окне Find in Files (Найдено в файлах).
36 Глава 2. Интегрированная среда разработки В окне Navigator (Навигатор) появится ветвь с результатами поиска. Если от- крыть ветвь (щёлкнув мышью по значку «+», который при открытии ветви пре- вратится в значок «-») и выбрать одну из строк с результатами поиска, то в соот- ветствующем файле искомый фрагмент текста будет выделен (Рис. 2.24). Рис. 2.24. Результаты поиска в окне Navigator (Навигатор). Команда Edit Replace (Правка -> Заменить) Эта команда позволяет в редактируемом файле заменить один фрагмент текс- та на другой. < Кнопка на панели инструментов . Комбинация «горячих» клавиш — <Ctrl+R>. После выбора этой команды появится диалоговое окно Замена, показанное на Рис. 2.25. Для осуществления замены следует в поле Что ввести фрагмент текста, кото- рый требуется заменить, в поле Чем — ввести фрагмент текста, на который требу- ется заменить. Если требуется, то следует установить соответствующие галочки в окошках Только слово целиком и С учетом регистра (см. Команда Edit Find (Правка Найти)).
2.1. Строка меню 37 Рис, 2.25, Диалоговое окно Замена. Чтобы заменить выделенный фрагмент текста, а затем перейти к следующему его вхождению, следует щёлкнуть по кнопке Заменить. Чтобы оставить выделенный фрагмент текста без изменений и найти следующее вхождение искомого фрагмента текста, следует щёлкнуть по кнопке Найти далее. Чтобы заменить все вхождения искомого фрагмента текста во всем файле, следует щёлкнуть по кнопке Заменить всё. Чтобы закрыть диалоговое окно без выполнения команды, следует щёлкнуть по кнопке Отменить. Команда Edit -> Toggle Bookmark (Правка -> Переключить закладку) Эта команда позволяет установить/удалить закладку в определённой строке файла, находящегося в активном окне редактора. Установленные закладки позволяют впоследствии осуществлять быстрый пе- реход к ним командой Edit Jump to Bookmark (Правка -> Перейти к закладке). В одном файле можно установить до десяти закладок. Чтобы закладки были видимы, следует сделать видимым поле с номерами строк. Для этого в настройках редактора (см. Команда Settings -» Editor (Настрой- ки Редактор)) следует установить галочку в окошке Show Line Number (Показы- вать номера строк). После выбора команды Edit -» Toggle Bookmark (Правка -> Переключить за- кладку) появится подменю со списком закладок. Установленные закладки отме- чены галочкой (Рис. 2.26). В верхней части этого подменю находится команда Clear АН (Очистить всё), позволяющая удалить ВСЕ закладки в файле. Чтобы установить закладку на какой-либо строке файла, следует установить курсор, щёлкнув мышью в любом месте этой строки. Затем выбрать команду Editor Toggle Bookmark (Правка -> Переключить закладку), выбрать номер за- кладки без галочки и щёлкнуть по ней мышью. Если щёлкнуть по закладке, отме- ченной галочкой, то закладка с этим номером будет установлена в текущей стро- ке, а там, где она была установлена ранее, закладка будет убрана. Чтобы убрать закладку с какой-либо строки, следует установить курсор в этой строке, щёлкнув мышью в любом месте этой строки. Затем выбрать команду Editor Toggle Bookmark (Правка -> Переключить закладку), выбрать номер за-
38 Глава 2. Интегрированная среда разработки Е1Й File Project Tools Settings Windows Help CodeVisionAVR - Led.prj I Edit 22 23 27 28 29 30 31 32 33 К D: \AVRTools\CodeVi$ion\Examples\Led\led.c 211 // I/O register definitions for AT 90, #include <90s8515.h> // quartz crystal frquency [Hz] l&efine xtal 3686400 // moving LED #define fmove PORTC led status // TIMER! overflow 1 & Clear All // the on unsigned char Puc. 2.26. Выбор команды Edit-* Toggle Bookmark (Правка -» Переключить закладку). frequency [HzJ I 2 * кладки, которая установлена в этой строке (она отмечена галочкой), и щёлкнуть по ней мышью. При этом галочка с этой закладки будет убрана. Команда Edit -> Jump to Bookmark (Правка -> Перейти к закладке) Эта команда позволяет осуществить переход к соответствующей закладке, ко- торая была установлена командой Edit -> Toggle Bookmark (Правка -> Переклю- чить закладку). Чтобы закладки были видимы, следует сделать видимым поле с номерами строк. Для этого в настройках редактора (см. Команда Setting? —> Editor (Настрой- ки -> Редактор)) следует установить галочку в окошке Show Line Number (Показы- вать номера строк). После выбора этой команды появится подменю со списком установленных закладок (Рис. 2.27). Если в данном файле установленных закладок нет, то подме- ню не появится. Чтобы перейти к какой-либо закладке файла, находящегося в активном окне редактора, следует выбрать команду Edit Jump to Bookmark (Правка —> Перейти к закладке), выбрать номер соответствующей закладки в подменю и щёлкнуть по
2.1. Строка меню 39 ней мышью. При этом курсор в активном окне редактора будет установлен в строку с соответствующей закладкой. Переход к закладкам можно осуществлять ТОЛЬКО в пределах файла, ко- торый находится в активном окне редактора. Project Tools Settings Windows Help Ctrl+ frequency [Hz] Ctrl+F Ctrl+R ® Toggle Bookmark Mes Alt+G Ctrl+M Ctrkl Ctrl+U PORTC output led sta-tu3=i Indent Blod Unirdent Block Ctrl+V Qrl+Del Ctrl+A Paste X Delete Ц Select All // the LED on Unsigned char // I/O register definitions for AT90. ^include <90s8515.h> Ctil+Z Shift 4<trkZ Find Find Next Find in Files Replace // quartz crystal frquency [Hz] ^define xtal 3686400 // moving LED #define fmove CodeVisionAVR - Led.pi j |j D:\AVRTools\CodeVision\ExarnplesM_ed\led bump to Bool mark Puc. 2.27. Выбор команды Edit -> Jump to Bookmark (Правка -> Перейти к закладке). Команда Edit Goto Line (Правка -э Перейти к строке) Эта команда позволяет осуществить переход к соответствующей строке фай- ла, который находится в активном окне редактора. Комбинация «горячих» клавиш — <Alt+G>. После выбора этой команды появится диалоговое окно Goto Line Number (Перейти к строке номер), по- казанное на Рис. 2.28. Чтобы перейти к какой-либо строке файла, в поя- вившемся диалоговом окне Goto Line Number (Перей- ти к строке номер) в окошке Enter New Line Number (Введите новый номер строки) необходимо ввести но- мер соответствующей строки и щёлкнуть по кнопке ОК. При этом курсор будет помещён в строку файла с соответствующим номером. Если введённый номер Рис. 2.28. Диалоговое окно Goto Line Number (Перейти к строке номер). строки превышает количество строк в файле, то курсор будет помещён в последнюю строку файла.
40 Глава 2. Интегрированная среда разработки Команда Edit -> Match Braces (Правка -» Выделить в скобках) Эта команда выделяет весь текст, находящийся в скобках. Комбинация «горячих» клавиш — <Ctrl+A>. Если курсор спозиционировать на открывающей скобке (или на одну позицию правее), то после выбора команды EditMatch Braces (Правка -> Выделить в скобках) будет выделена часть текста до соответствующей закрывающей скобки (Рис. 2.29). Если курсор спозиционировать на закрывающей скобке (или на одну пози- цию левее), то после выбора команды Edit Match Braces (Правка -> Выделить в скобках) будет выделена часть текста до соответствующей открывающей скобки. Нажав любую клавишу или щёлкнув мышью, можно убрать выделение. Команда удобна при просмотре функций с глубокими вложениями. Рис. 2.29. Выделение части текста до соответствующей закрывающей скобки. 2.1.3. Меню Project (Проект) В состав меню Project (Проект) входит несколько команд. В Табл. 2.6 даётся их краткое описание. Таблица 2.6. Команды меню Project (Проект) Команда Действие Compile (Компилировать) Компилирует проект Маке (Построить) Осуществляет построение проекта Stop Compilation (Остановить компиляцию) Останавливает процесс компиляции
2.1. Строка меню 41 (продолжение) Команда Действие Infomation (Информация) Выводит окно с информацией Notes (Примечания) Осуществляет доступ к файлу Notes (Примечания) Configure (Конфигурировать) Позволяет настроить конфигурацию проекта Команда Project -> Compile (Проект Компилировать) Команда позволяет скомпилировать открытый в данный момент проект. □ ^1 Кнопка на панели инструментов ! . Комбинация «горячих» клавиш — <F9>. Пока проект не создан, эта команда (и кнопка на панели инструментов) не активна. Подробнее о компиляции проекта рассказано ниже (см. Компиляция проек- та). Команда Project Маке (Проект -> Построить) Эта команда позволяет построить открытый в данный момент проект. Кнопка на панели инструментов Комбинация «горячих» клавиш — <Shift+F9>. Пока проект не создан, эта команда (и кнопка на панели инструментов) не активна. Для того чтобы построить проект, следует выбрать команду Project Маке (Проект -> Построить), как показано на Рис. 2.30, или щёлкнуть по соот- ветствующей кнопке на панели инструментов. Подробнее о построении проекта рассказано ниже (см. Построение проекта). File Edit Navigator я cS Tools Settings Windows Help Compile fe D:\AVRTools\CodeVision\Examples\Led\led The LEDs anodes Notes §4 Configure Messages Ctrl+N 17 connect the PORTC and LEDS together with a 10-wire cabl^ Puc. 2.30. Выбор команды Project Make (Проект -> Построить).
42 Глава 2. Интегрированная среда разработки Команда Project Stop Compilation (Проект Остановить компиляцию) Эта команда позволяет в любой момент прервать процесс компиляции, запу- щенный командами Project -> Compile (Проект -> Компилировать) или Project -> Make (Проект -> Построить). Кнопка на панели инструментов Qj. Пока процесс компиляции не запущен, эта команда (и кнопка на панели инс- трументов) не активна. Для того чтобы прервать процесс компиляции, следует выбрать команду Project -> Stop Compilation (Проект -> Остановить компиляцию) или щёлкнуть по соответствующей кнопке на панели инструментов. Современные компьютеры выполняют компиляцию с такой огромной ско- ростью, что при небольших объёмах программы данная команда практи- чески бесполезна. Команда Project Information (Проект -> Информация) Эта команда выводит окно Information (Информация), показывая результаты выполнения команды Project Compile (Проект -> Компилировать) (см. Компи- ляция проекта) или Project -> Make (Проект -> Построить) (см. Построение проек- та), которая выполнялась последней. Кнопка на панели инструментов jQj]. Пока проект не скомпилирован (Compile) или не построен (Маке), эта коман- да (и кнопка на панели инструментов) не активна. Для того чтобы вывести окно Information (Информация), следует выбрать ко- манду Project Information (Проект -> Информация) или щёлкнуть по соответствующей кнопке на панели инструментов. Команда Project -> Notes (Проект -> Примечания) При создании каждого проекта IDE CodeVisionAVR создаёт текстовый файл, где можно поместить примечания и комментарии к проекту. Этот файл можно от- крыть в области редактирования, вызвав команду Project -> Notes (Проект -> При- мечания). Его можно отредактировать, используя стандартные команды меню Edit (Правка). Файл автоматически сохраняется при закрытии проекта (см. Ко- манда File -> Close (Файл Закрыть)) или при выходе из программы CodeVisionAVR (см. Команда File -> Exit (Файл -> Выход)). Этот файл также можно отредактировать и просмотреть в любом другом текс- товом редакторе. Комбинация «горячих» клавиш — <Ctrl+N>. При выборе команды Project -> Notes (Проект -> Примечания) в области ре- дактирования откроется окно редактора Project Notes (Примечания проекта) с
2.1. Строка меню 43 именем текущего проекта (Рис. 2.31). Этот файл можно также открыть из окна Navigator (Навигатор), щёлкнув по иконке . С^==> Файл Notes (Примечания) ВСЕГДА сохраняется в директории проекта. Причём его имя ВСЕГДА совпадает с именем проекта (файла с расшире- нием .prj), и он ВСЕГДА имеет расширение .txt. В CodeVisionAVR этот файл НЕЛЬЗЯ сохранить под другим именем и в другом месте. Рис. 2.31. Открытие файла Notes (Примечания) из окна Navigator (Навигатор). Команда Project Configure (Проект -> Конфигурировать) Эта команда позволяет в любой момент изменить конфигурацию проекта. Кнопка на панели инструментов . Пока проект не создан или не открыт, эта команда (и кнопка на панели инс- трументов) не активна. Для того чтобы изменить конфигурацию текущего проекта, следует выбрать команду ProjectConfigure (Проект -> Конфигурировать) или щёлкнуть по соот- ветствующей кнопке на панели инструментов. После выбора этой команды появится диалоговое окно Configure Project (Кон- фигурация проекта) с именем текущего проекта (Рис. 2.32). Подробнее о конфигурировании проекта рассказано ниже (см. Конфигуриро- вание проекта). Одновременно можно работать только с одним проектом. При открытии нового текущий проект будет закрыт автоматически.
44 Глава 2, Интегрированная среда разработки Рис. 2.32. Диалоговое окно Configure Project (Конфигурация проекта) с именем проекта. 2.1.4. Меню Tools (Инструменты) В состав меню Tools (Инструменты) входит несколько команд. В Табл. 2.7 да- ётся их краткое описание. Таблица 2.7. Команды меню Tools (Инструменты) Команда Действие CodeWizardAvr Запускает автоматический генератор программ CodeWizardAVR Debugger (Отл адч и к) Запускает отладчик Chip Programmer (Программатор) Запускает программатор для чипов AVR Terminal (Терминал) Запускает терминал последовательной связи Configure (Конфигурировать) Позволяет добавлять или удалять пользовательские программы из меню Tools (Инструменты) Команда Tools CodeWizardAVR (Инструменты -> CodeWizardAVR) Эта команда запускает автоматический генератор программ CodeWizardAVR. Кнопка на панели инструментов
2.1. Строка меню 45 Комбинация «горячих» клавиш — <Shift+F2>. Для того чтобы запустить автоматический генератор программ CodeWizardAVR, следует выбрать команду Tools CodeWizardAVR (Инструменты CodeWizardAVR) или щёлкнуть по соответствующей кнопке на панели инструментов. После выбора этой команды появится диалоговое окно CodeWizardAVR, показан- ное на Рис. 2.33. Подробнее о работе с автоматическим ге- нератором программ CodeWizardAVR будет рассказано ниже (см. Автоматический генера- тор программ Code WizardA VR). Команда Tools -> Debugger (Инструменты Отладчик) Эта команда запускает отладчик Atmel AVR Studio. . Кнопка на панели инструментов Ж| . Комбинация «горячих» клавиш — <Shift+F3>. Рис. 2.33. Диалоговое окно CodeWizardAVR. Для того чтобы запустить отладчик Atmel AVR Studio, следует выбрать команду Tools Debugger (Инструменты От- ладчик) или щёлкнуть по соответствующей кнопке на панели инструментов. После выбора этой команды будет запущен отладчик Atmel AVR Studio, в ко- тором можно будет отладить программу, написанную в CodeVisionAVR. Подробнее об отладке программ с помощью AVR Studio будет рассказано ниже (см. Работа с отладчиком AVR Studio). Команда Tools Chip Programmer (Инструменты -э Программатор чипов) Эта команда запускает программатор для записи программы непосредственно в чип AVR. । Кнопка на панели инструментов . Комбинация «горячих» клавиш — <Shift+F4>. Для того чтобы запустить программатор, следует выбрать команду Tools -> Chip Programmer (Инструменты -> Программатор чипов) или щёлкнуть по соответс- твующей кнопке на панели инструментов. После выбора команды появится диа- логовое окно CodeVisionAVR Chip Programmer (CodeVisionAVR программатор чи- пов) для выбранного программатора (см. Команда Settings -> Programmer (На- стройки —> Программатор)). Например, если выбран программатор Atmel STK500/AVRISP, то появится диалоговое окно, показанное на Рис. 2.34.
46 Глава 2. Интегрированная среда разработки CodeVisionAVR Chip Programmer Atmel STK5OO/AVRISP [X j File Edit Program Read Compare Help jfcfr Program All | Q? Re$etChip| rEEPROM Start.fo h End: pFF h Checksum: FOOOh Chip: | AJmegal 28 ж | -FLASH-------------------------1 Startfo h End: |FFFF h Checksum: OOOOh Chip Programming Options । FLASH Lock Bits™.. <• Ng Protection C Programming disabled Programming and Verification disabled “Boot Lock Bit 0-1 B01=1 В 02=1 | Г B01»0B02=1 | Г B01=0B02»0 | Г B01=1 В 02=0 I ‘Boot Lock Bit 1— G B11«1 В 12=1 С B11-0B12-1 С B11-0B12-0 Г B11-1 В 12=0 Fuse Bit(s): Г CKSELOO Г CKSEL1-0 Г CKSEL2=0 Г CKSEL3=0 Г SUTO-O Г SUT1-0 Г BODEN=O Г BODLEVEL-O Г BOOTRST-O Г BOOTSZM Г BOOTSZ1=0 Г EESAVE=0 Г СКОРТ-О П JTAGEN=0 Г OCDEN=0 f7 Check Signature Г" Check Erasure Г Preserve EEPROM P Verify Puc. 2.34. Диалоговое окно CodeVisionAVR Chip Programmer (CodeVisionAVR программатор чипов) для программатора Atmel STK500/AVRISP. Подробнее о программировании микроконтроллеров AVR рассказано ниже (см. Запись программы в чип AVR). Команда Tools -> Terminal (Инструменты -> Терминал) Эта команда запускает терминал последовательной связи. Кнопка на панели инструментов Ml • Комбинация «горячих» клавиш — <Shift+F5>. Для того чтобы запустить терминал, следует выбрать команду Tools -> Terminal (Инструменты -> Терминал) или щёлкнуть по соответствующей кнопке на пане- ли инструментов. После выбора этой команды в области редактирования появится окно Terminal (Терминал) (Рис. 2.35).
2.1. Строка меню 47 Рис. 2.35. Окно Terminal (Терминал). Подробнее о работе с терминалом см. Терминал последовательной связи. Команда Tools -> Configure (Инструменты -> Конфигурировать) Эта команда позволяет добавлять или удалять пользовательские программы из меню Tools (Инструменты). После выбора этой команды откроется диалоговое окно Configure Tools (Кон- фигурирование инструментов), показанное на Рис. 2.36. Рис. 2.36. Диалоговое окно Configure Tools (Конфигурирование инструментов). Чтобы добавить программу к меню Tools (Инструменты), следует щёлкнуть по кнопке Add (Добавить). При этом откроется диалоговое окно Add File То Tools Menu (Добавить файл к меню Tools). В диалоговом окне Add File То Tools Menu (Добавить файл к меню Tools) следу- ет выбрать необходимый файл и щёлкнуть по кнопке Открыть (Рис. 2.37). Для примера добавим к меню Tools (Инструменты) текстовый редактор PSPad.
48 Глава 2. Интегрированная среда разработки Рис. 2.37. Выбор программы в диалоговом окне Add File То Tools Menu (Добавить файл к меню Tools). После этого в диалоговом окне Configure Tools (Конфигурирование инстру- ментов) появится строка с добавленным файлом PSPad.exe (Рис. 2.38), после чего следует щёлкнуть по кнопке ОК. Рис. 2.38. Список программ, добавленных к меню Tools (Инструменты). Теперь эта программа будет присутствовать в меню Tools (Инструменты), от- куда её легко вызвать, просто щёлкнув по ней (Рис. 2.39). Кроме того, для запуска этой программы будут автоматически назначены «горячие» клавиши по порядку с <Shift+F2> по <Shift+F10>. Вменю Tools (Инструменты) можно добавлять до ПЯТИ программ.
2.1. Строка меню 49 Рис. 2.39. Вызов программы PSPad из меню Tools (Инструменты). В диалоговом окне Configure Tools (Конфигурирование инструментов) можно изменить настройки для выбранной программы. Для этого выберите программу, а затем щёлкните по кнопке Settings (Настройки) (Рис. 2.40). Рис. 2.40. Изменение настроек программы PSPad. Откроется диалоговое окно Tool Settings (Настройки инструмента) (Рис. 2.41).
50 Глава 2. Интегрированная среда разработки Рис. 2.41. Диалоговое окно Tool Settings (Настройки инструмента). В этом диалоговом окне можно изменить следующие параметры добавленно- го инструмента: • Tool Name (Имя инструмента); • Tool Directory and FileName (Директория инструмента и имя файла), причём можно ввести путь непосредственно в это окошко или щёлкнуть по кнопке . При этом появится диалоговое окно Tool Directory and FileName (Директория программы и имя файла), показанное на Рис. 2.42. Рис. 2.42. Диалоговое окно Tool Directory and FileName (Директория программы и имя файла). В этом окне с помощью мыши можно выбрать соответствующую директорию и имя файла и щёлкнуть по кнопке Открыть. Выбранная директория и имя файла появятся в окошке Tool Directory and FileName (Директория программы и имя файла). • Command Line Parameters (Параметры командной строки); • Working Directory (Рабочая директория выбранной программы), причём можно ввести путь непосредственно в это окошко или щёлкнуть по кнопке fe::l •
2.1. Строка меню 51 При этом появится окно Select the Working Directory (Выбор рабочей директо- рии) со списком директорий (Рис. 2.43). Рис. 2.43. Окно Select the Working Directory (Выбор рабочей директории) со списком директорий. В этом окне можно выбрать соответствующую директорию с помощью мыши и щёлкнуть по кнопке ОК. Выбранная директория появится в окошке Working Directory (Рабочая директория). Изменения настроек можно сохранить, щёлкнув по кнопке ОК, или отме- нить, щёлкнув по кнопке Cancel (Отменить). Чтобы удалить пользовательскую программу из меню Tools (Инструменты), следует выбрать команду Tools Configure (Инструменты Конфигурировать). Затем в появившемся диалоговом окне Configure Tools (Конфигурирование инс- трументов) выбрать соответствующую программу и щёлкнуть по кнопке Remove (Удалить) (Рис. 2.44). Рис. 2.44. Удаление программы из меню Tools (Инструменты).
52 Глава 2, Интегрированная среда разработки 2.1.5. Меню Settings (Настройки) В состав меню Settings (Настройки) входит несколько команд. В Табл. 2.8 даёт- ся их краткое описание. Таблица 2.8. Команды меню Settings (Настройки) Команда Действие General (Общие) Позволяет изменить общие настройки Editor (Редактор) Позволяет изменить настройки редактора Assembler (Ассемблер) Позволяет изменить настройки ассемблера Debugger (Отладчик) Позволяет изменить настройки отладчика Programmer (Программатор) Позволяет изменить настройки программатора Terminal (Терминал) Позволяет изменить настройки терминала Команда Settings -> General (Настройки Общие) Эта команда позволяет изменить общие настройки CodeVisionAVR. После выбора этой команды появится диалоговое окно General Settings (Об- щие настройки), показанное на Рис. 2.45. Рис. 2.45. Диалоговое окно General Settings (Общие настройки). В этом диалоговом окне можно разрешить/запретить (соответственно устано- вить/снять галочку) следующие опции: • Show Toolbar (Показывать панель инструментов); • Show Navigator (Показывать окно навигатора); • Show Messages (Показывать окно сообщений); • Show Information (Показывать окно информации после компиляции или построения проекта). Изменения общих настроек можно сохранить, щёлкнув по кнопке ОК, или отменить, щёлкнув по кнопке Cancel (Отменить). Для начинающих лучше установить галочки во всех опциях.
2.1. Строка меню 53 Команда Settings -> Editor (Настройки -> Редактор) Эта команда позволяет изменить настройки редактора CodeVisionAVR. После выбора этой команды появится диалоговое окно Editor Settings (На- стройки редактора), показанное на Рис. 2.46. Рис. 2.46. Диалоговое окно Editor Settings (Настройки редактора). В этом диалоговом окне можно разрешить/запретить (соответственно устано- вить/снять галочку) следующие опции: • Syntax Highlighting (Подсвечивать синтаксис). Разрешение этой опции позволяет подсвечивать текст программы, в соот- ветствии с синтаксисом Си, определёнными цветами (Рис. 2.47), которые можно выбрать в поле Colors (Цвета). Это делает текст программы более удобочитаемым и позволяет уменьшить количество ошибок. а)
54 Глава 2. Интегрированная среда разработки б) Рис. 2.47. Опция Syntax Highlighting (Подсвечивать синтаксис) разрешена (а) и запрещена (б). • Show Line Numbers (Показывать номера строк). Разрешение этой опции выводит слева в окне редактирования столбец с номерами строк (Рис. 2.48). Это позволяет легче ориентироваться в боль- ших текстах программ. а) б) Рис. 2.48. Опция Show Line Numbers (Показывать номера строк) разрешена (а) и запрещена (б).
2.1. Строка меню 55 • Auto Indent (Автоматический отступ). Разрешение этой опции позволяет при написании программы делать авто- матические отступы после нажатия клавиши <Enter> (Ввод) (Рис. 2.49). Это облегчает редактирование файлов. Auto Load Modified (Автоматически загружать изменения). б) Рис. 2.49. Текущая позиция курсора после нажатия в конце предыдущей строки клавиши <Enter> (Ввод) при разрешённой (а) и запрещённой (б) опции Auto Indent (Автоматический отступ). • Auto Load Modified (Автоматически загружать изменения). Разрешение этой опции позволяет автоматически перезагружать открытые файлы, которые были изменены. Если в IDE CodeVisionAVR открыт ка- кой-то файл, а затем в этот файл вносятся и сохраняются изменения ка- кой-то другой внешней программой, то при возвращении в IDE CodeVisionAVR эти изменения будут внесены автоматически, если опция Auto Load Modified (Автоматически загружать изменения) разрешена. Если эту опцию запретить, то при возвращении в IDE CodeVisionAVR будет вы- ведено окно Information (Информация). В этом окне будет сообщено, что
56 Глава 2. Интегрированная среда разработки соответствующий файл был изменён, и требуется подтверждение на пере- загрузку изменённого файла (Рис. 2.50). Рис. 2.50. Окно с информацией об изменении соответствующего файла. Если щёлкнуть по кнопке Yes, то в файл, открытый в IDE CodeVisionAVR, бу- дут внесены соответствующие изменения (файл будет перезагружен). Если щёлкнуть по кнопке No, то этот файл останется без изменений. В диалоговом окне Editor Settings (Настройки редактора) (Рис. 2.46) также можно изменить следующие настройки редактора: • Tab size — позволяет задать количество пробелов, которое будет вставлено в текст при нажатии клавиши <ТаЬ>. • Font (Шрифт) — позволяет выбрать шрифт редактора. Если щёлкнуть по кнопке, расположенной рядом со словом Font, то появится диалоговое ок- но Шрифт (Рис. 2.51). По умолчанию (Default) установлен шрифт Courier New. Для русскоязычных пользователей набор символов рекомендуется ус- тановить Кириллический. В диалоговом окне Editor Settings (Настройкиредактора) на кнопке, распо- ложенной рядом со словом Font, буква А выводится шрифтом, который ус- тановлен для редактора. Рис. 2.51. Диалоговое окно Шрифт.
2.1. Строка меню 57 • Colors (Цвета) — позволяет задать цвета для подсветки синтаксиса (см. оп- цию Syntax Highlighting (Показывать подсветку синтаксиса)). Здесь можно изменить цвета следующих составляющих: — Background (Фон) — задаёт цвет фона; — Text (Текст) — задаёт цвет текста; — С Keywords (Ключевые слова Си) — задаёт цвет ключевых слов Си (см. Зарезервированные ключевые слова); — Preprocessor (Препроцессор) — задаёт цвет команд препроцессора (см. Препроцессор); — Storage modif. (Место хранения модификатора) — задаёт цвет места хра- нения модификатора (flash, eeprom или const); — Strings (Строки) — задаёт цвет строковых констант; — Comments (Комментарии) — задаёт цвет комментариев. Если опция Syntax Highlighting (Показывать подсветку синтаксиса) запре- щена, то весь текст программы выводится цветом, заданным опцией Text (Текст), на фоне, заданном опцией Background (Фон). Изменения конфигурации редактора можно сохранить, щёлкнув по кнопке ОК, или отменить, щёлкнув по кнопке Cancel (Отменить). Щёлкнув по кнопке Default (По умолчанию) можно восстановить настройки редактора, предусмотренные по умолчанию. Установленный шрифт и цвета для Background (Фон) и Text (Текст) оди- наковые для редактора и терминала. Команда Settings -> Assembler (Настройки -> Ассемблер) Эта команда позволяет изменить настройки ассемблера. После выбора этой команды появится диалоговое окно Assembler Settings (На- стройки ассемблера), показанное на Рис. 2.52. Рис. 2.52. Диалоговое окно Assembler Settings (Настройки ассемблера). В этом диалоговом окне можно разрешить/запретить (соответственно устано- вить/снять галочку) опцию Expand Macros in .1st file (Разворачивать макросы в файле .1st). При разрешении этой опции в файл с расширением .asm добавляется директива ассемблера .LISTMAC (Рис. 2.53), благодаря которой в листинге (в файле с расширением .1st) будет показано содержимое макросов. По умолчанию
58 Глава 2. Интегрированная среда разработки (без директивы .LISTMAC) в листинге показываются только вызов макроса и пе- редаваемые параметры, а тело макроса не показывается (Рис. 2.54). а) б) Рис. 2.53. Результирующие файлы .asm (а) и .1st (б) при разрешённой опции Expand Macros in .1st file (Разворачивать макросы в файле .1st) — макрос_____PUTW1R развёрнут. Подробнее о директивах и макросах ассемблера см. систему помощи бесплат- ной программы AVR Studio от Atmel, которую можно скачать на сайте разработчи- ков микроконтроллеров AVR http://www.atmel.com. В диалоговом окне Assembler Settings (Настройки ассемблера) (Рис. 2.52) так- же можно выбрать, какой файл автоматически будет открыт редактором в случае ассемблерной ошибки. Для этого в окошке On Assembler Error(s) (При ошибке ас- семблера) следует выбрать одну из двух опций: • Open the .asm file (Открыть файл с расширением .asm); • Open the .1st file (Открыть файл с расширением .1st). Изменения настроек ассемблера можно сохранить, щёлкнув по кнопке ОК, или отменить, щёлкнув по кнопке Cancel (Отменить).
2.1. Строка меню 59 б) Рис. 2.54. Результирующие файлы .asm (а) и .1st (б) при запрещённой опции Expand Macros in .1st file (Разворачивать макросы в файле .1st) — макрос______PUTW1R свёрнут. Команда Settings -> Debugger (Настройки -> Отладчик) Эта команда позволяет изменить настройки отладчика. После выбора этой команды по- явится диалоговое окно Debugger Settings (Настройки отладчика), по- казанное на Рис. 2.55. Компилятор CodeVisionAVR версии 1.24.1х Standart предназначен для исполь- зования вместе с отладчиком Atmel AVR Studio версий 3 и 4.06 (или бо- лее поздней). Разумеется, что для использования отладчика на ком- пьютере должна быть установлена AYR Studio соответствующей вер- Рис. 2.55. Диалоговое окно Debugger Settings (Настройки отладчика). сии. В диалоговом окне Debugger Settings (Настройки отладчика) в выпадающем списке в окошке Debugger (Отладчик) следует вы- брать версию Atmel AYR Studio (Рис. 2.56). В окошке Directory and Filename (Директория и имя файла) следует указать путь к файлу AVR Studio.exe, причём можно ввести Рис. 2.56. Выбор версии Atmel AVR Studio. путь непосредственно в это окош- ко или щёлкнуть по кнопке . При этом появится диалоговое окно Debugger Directory and FileName (Директория отладчика и имя файла), показанное на Рис. 2.57. В этом окне можно выбрать соответствующий файл и щёлкнуть по кнопке Открыть. Выбранная директория и файл появятся в окошке Directory and Filename (Директория и имя файла).
60 Глава 2. Интегрированная среда разработки Рис. 2.57. Диалоговое окно Debugger Directory and FileName (Директория отладчика и имя файла). Изменения настроек отладчика можно сохранить, щёлкнув по кнопке ОК, или отменить, щёлкнув по кнопке Cancel (Отменить). После изменения настроек отладчика и нажатия кнопки OK CodeVisionAVR выведет окно Warning (Предупреждение) (Рис. 2.58). Warning CodeVisionAVR will produce an extended COFF object file. Please make sure to use AVR Studio 4.06 or later. Earlier versions of AVR Studio 4 do not support the extended COFF object file format and will not work properly. Puc. 2.58. Окно Naming (Предупреждение). — В этом окне говорится следующее: CodeVisionAVR произведёт объектный файл с расширением СОЕ Убедитесь, что используется версия AVR Studio 4.06 или более поздняя. Более ранние версии AVR Studio 4 не поддерживают объектный файловый формат с расширением COF и будут работать неправильно. Команда Settings -> Programmer (Настройки -> Программатор) Эта команда позволяет выбрать тип используемого внутрисхемного програм- матора и компьютерный порт, к которому он будет подключён. После выбора этой команды появится диалоговое окно Programmer Settings (Настройки программатора), показанное на Рис. 2.59.
2.1. Строка меню 61 Programmer Settings AVR Chip Programmer Type: | Atmel STK500/AVRISP \/ QK~ | X Cancel J Puc. 2.59. Диалоговое окно Programmer Settings (Настройки программатора) для последовательных программаторов. В этом диалоговом окне в выпадающем списке в окошке AVR Chip Programmer ТУре (Тип программатора чипов AVR) (Рис. 2.60) можно выбрать следующие ти- пы внутрисхемных программаторов: • Kanda Systems STK200+/300; • Atmel STK500/AVRISP; • Atmel AVRProg (AVR910); • Dontronics DT006; • Vogel Elektronik VTEC-ISP; • Futurlec JRAVR; • MicroTronics ATCPU/Mega2000. Progiammei Settings AVR Chip Programmer Type: | Dontronics D TOPS "yj &melSTI^/AVRISP 1X7 Atmel AVRProg (AVR 910) Dontronics DT006 Vogel Elektronik VTEC-ISP Futurlec JRAVR I MicroTronics ATCPU/Mega2000 ✓ OK ] X Cancel | ----------------------------— Puc. 2.60. Выбор типа используемого внутрисхемного программатора. Если разрешена опция Alhiegal69 CKDIV8 Fuse Warning (ATmegal69 CKDIV8 предохранитель предупреждение) (Рис. 2.61), то разрешается генерация преду- преждения о том, что дальнейшее снижение напряжения последовательного про- граммирования невозможно для платы ATmegal69 Engineering Sample, если пре- дохранитель CKDIV8 запрограммирован в 0. Для обычных чипов ATmegal69 эта опция должна быть запрещена.
62 Глава 2. Интегрированная среда разработки Рис. 2.61. Опция ABnegal69 CKDIV8 Fuse Warning (ATmegal69 CKDIV8 предохранитель предупреждение) разрешена. Программаторы STK500, AVRISP и AVRProg используют последовательный коммуникационный порт RS232C. В диалоговом окне Programmer Settings (Настройки программатора) в выпада- ющем списке в окошке Communication Port (Коммуникационный порт) можно выбрать порт СОМ1...СОМ6, к которому подсоединён программатор (Рис. 2.62). Рис. 2.62. Выбор последовательного порта для подключения программатора. Программаторы STK200+, STK300, DT006, VTEC-ISP, JRAVR, ATCPU и Mega2000 используют параллельный принтерный порт. В диалоговом окне Programmer Settings (Настройки программатора) в выпада- ющем списке в окошке Printer Port (Принтерный порт) (Рис. 2.63) можно вы- брать порт, к которому подсоединён программатор: • LPT1, по базовому адресу 378h; • LPT2, по базовому адресу 278h; • LPT3, по базовому адресу 3BCh. Значение Delay Multiplier (Множитель задержки) можно увеличить в случае возникновения проблем при программировании на очень быстрых машинах (Рис. 2.64). Разумеется, это увеличит общее время программирования.
2.1. Строка меню 63 Programmer Settings AVR Chip Programmer Type: | Kanda Systems STK200-I-/300 v| |LpT2;278h 1LPT1: 378h 5 Printer Port: LPT2: 278h ILPT3: 3BCh CKDIV8 Fuse Warning Delay Multiplier: Г* ATmega169 X Cancel | Puc. 2.63. Диалоговое окно Programmer Settings (Настройки программатора) для параллельных программаторов. Рис. 2.64. Опция Delay Multiplier (Множитель задержки) в диалоговом окне Programmer Settings (Настройки программатора) для параллельных программаторов. Изменения настроек можно сохранить, щёлкнув по кнопке ОК, или отме- нить, щёлкнув по кнопке Cancel (Отменить). Команда Settings -> Terminal (Настройки -» Терминал) Эта команда позволяет изменить настройки терминала. После выбора этой команды появится диалоговое окно Terminal Settings (На- стройки терминала), показанное на Рис. 2.65. В диалоговом окне Terminal Settings (Настройки терминала) можно выбрать: • Port — компьютерный коммуникационный порт, используемый термина- лом: СОМ1...СОМ6; • Baud rate — скорость передачи, используемая для связи: 110...115200 бод; • Data bits — количество битов данных, используемых при приёме и переда- че: 5...8; • Stop bits — количество стоповых битов, используемых при приёме и пере- даче: 1, 1.5 или 2;
64 Глава 2. Интегрированная среда разработки • Parity — использование контроля чётности при приёме и передаче: None (Не используется), Odd (Нечётная), Even (Чётная), Mark (Метка) или Space (Пробел); • Emulation — тип эмулируемого терминала: TTY, VT52 или VT100; • Handshaking — тип установления связи, используемый при коммуникации: None (Не используется), Аппаратно (CTS или DTR) или Программно (Xon/Xoff); • Append LF — возможность добавлять символы LF (символ новой строки) после символов CR (символ возврата каретки) на приёме (On Reception) и передаче (On Transmission); • Echo — разрешить или запретить эхоконтроль переданных символов. Рис. 2.65. Диалоговое окно Terminal Settings (Настройки терминала). Изменения настроек можно сохранить, щёлкнув по кнопке ОК, или отме- нить, щёлкнув по кнопке Cancel (Отменить). 2.1.6. Меню Windows (Окна) Для того чтобы вызвать выпадающее меню Windows (Окна), следует в строке меню щёлкнуть по названию Windows (Окна). В состав меню Windows (Окна) входит несколько команд. В Табл. 2.9 даётся их краткое описание. Все эти команды позволяют в области редактирования упоря- дочить окна всех файлов, открытых в данный момент. Таблица 2.9. Команды меню Windows (Окна) Команда Действие Tile Horizontal (Горизонтально) Располагает окна горизонтально Tile Vertical (Вертикально) Располагает окна вертикально Cascade (Каскадом) Располагает окна каскадом
2.1. Строка меню 65 Кроме того, внизу выпадающего меню Windows (Окна) приводится список файлов, открытых в данный момент в области редактирования. Активный файл помечен галочкой. Команда Windows -> Tile Horizontal (Окна Горизонтально) Эта команда в области редактирования упорядочивает окна всех файлов, от- крытых в данный момент, располагая их горизонтально. Кнопка на панели инструментов . После выбора этой команды все окна в области редактирования будут распо- ложены горизонтально (Рис. 2.66). Активное в данный момент окно после выбора команды Windows -> Tile Horizontal (Окна -> Горизонтально) располагается на самом верху. Рис. 2.66. Расположение окон после выбора команды Windows Где Horizontal (Окна -> Горизонтально). Команда Windows Tile Vertical (Окна -э> Вертикально) Эта команда в области редактирования упорядочивает окна всех файлов, от- крытых в данный момент, располагая их вертикально. Кнопка на панели инструментов д. После выбора этой команды все окна в области редактирования будут распо- ложены вертикально (Рис. 2.67). Активное в данный момент окно после выбора команды Windows Tile Vertical (Окна Вертикально) располагается слева.
66 Глава 2. Интегрированная среда разработки Рис, 2.67. Расположение окон после выбора команды Windows-* Tile Vertical (Окна -* Вертикально). Команда Windows -> Cascade (Окна -> Каскадом) Эта команда в области редактирования упорядочивает окна всех файлов, от- крытых в данный момент, располагая их каскадом. Кнопка на панели инструментов Ж1 . После выбора этой команды все окна в области редактирования будут распо- ложены каскадом (Рис. 2.68). Активное в данный момент окно после выбора команды Windows —> Tile Cascade (Окна Каскадом) располагается на переднем плане. Рис. 2.68. Расположение окон после выбора команды Windows -> Cascade (Окна -> Каскадом).
2.1. Строка меню 67 2.1.7. Меню Help (Помощь) В состав меню Help (Помощь) входит несколько команд. В Табл. 2.10 даётся их краткое описание. Таблица 2.10. Команды меню Help (Помощь) Команда Действие Help Topics (Темы помощи) Выводит содержание помощи Getting Started with CodeVisionAVR (Начало работы с CodeVisionAVR) Осуществляет переход к соответствующему документу AVR Data Sheets (Описания AVR) Осуществляет переход на сайт фирмы Atmel, на страницу с описаниями микроконтроллеров AVR Export License (Экспорт лицензии) Позволяет осуществить экспорт лицензии на другой компьютер HP InfoTech on the Web (HP InfoTech в Интернете) Осуществляет переход на домашнюю страницу сайта фирмы HP InfoTech — разработчика CodeVisionAVR E-Mail HP InfoTech (Электронная почта HP InfoTech) Позволяет написать и отправить электронное письмо разработчику CodeVisionAVR About CodeVisionAVR Выводит информацию о программе Команда Help -э Help Topics (Помощь -> Темы помощи) Эта команда выводит содержание помощи, кото- рая имеется в программе. Кнопка на панели инстру- 9 I ментов . Комбинация «горячих» клавиш — <F1>. Для того чтобы вывести содержание помощи, следу- ет выбрать команду Help Help Topics (Помощь -> Те- мы помощи) или щёлкнуть по соответствующей кноп- ке на панели инструментов. После выбора этой ко- манды появится диалоговое окно Справка: CodeVisionAVR С Compiler Help (Справка: помощь по компилятору Си CodeVisionAVR), показанное на Рис. 2.70. Рис. 2.70. Диалоговое окно Справка: CodeVisionAVR CCompiler Help (Справка: помощь по компилятору Си CodeVisionAVR).
68 Глава 2. Интегрированная среда разработки В этом диалоговом окне отображается список категорий разделов помощи. Чтобы открыть группу разделов, относящихся к какой-либо теме, следует дважды щёлкнуть значок соответствующей книги. Чтобы закрыть книгу, снова дважды щёлкните по её значку. Чтобы открыть раздел, нажмите кнопку Показать или дважды щёлкните по нему (Рис. 2.71). После этого будет открыт соответствующий раздел помощи (Рис. 2.72). Чтобы распечатать соответствующий раздел, выберите его и нажмите кнопку Печать. Рис. 2.71. Выбор раздела помощи. CodeVisionAVR С Compiler Help [ - || □ || X | Файл Правка Закладка Параметры Справка Содержание] Указатель | Назад | Печать | Code Vision A VR VERSION 1.24 1 CodeVisionAVR is a C cross-compiler Integrated Development Environment and Automatic Program Generator designed for the Atmel AVR family of microcontrollers. The program is designed to run under the Windows 95, 98, Me, NT 4, 2000 and XP operating systems. The C cross-compiler implements nearly all the elements of the ANSI C language, as allowed by the AVR architecture, with some features added to take advantage of specificity of the AVR architecture and the embedded system needs. Puc. 2.72. Открытый раздел помощи Short Presentation (Короткая презентация).
2.1. Строка меню 69 Команда Help -> Getting Started with CodeVisionAVR (Помощь -> Начало работы с CodeVisionAVR) Эта команда при наличии подключённого Интернета осуществляет переход к соответствующему документу. После выбора этой команды будет запущен Internet Explorer и осуществлён переход по адресу: www.atmel.com/dyn/resources/prod documents/DQC2500.PDE В результате будет открыт документ AVR033: Getting Started with the CodeVisionAVR C Compiler (Начало работы с компилятором Си CodeVisionAVR) (Рис. 2.73). Рис. 2.73. Открытый документ AVR033: Getting Started with the CodeVisionAVR C Compiler (Начало работы с компилятором Си CodeVisionAVR). Команда Help AVR Data Sheets (Помощь -> Описания AVR) Эта команда при наличии подключённого Интернета осуществляет переход на сайт Atmel, на страницу с описаниями микроконтроллеров AVR. После выбора команды будет запущен Internet Explorer и осуществлен переход по адресу: www.atmel.com/dyn/products/datasheets.asp7family id=607 на сайт компании Atmel. Команда Help Export License (Помощь -> Экспорт лицензии) Эта команда позволяет экспортировать лицензию на программу с одного ком- пьютера на другой. Подробнее о переносе лицензии с одного компьютера на другой см. Экспорт лицензии на другой компьютер.
70 Глава 2, Интегрированная среда разработки Команда Help -> HP InfoTech on the Web (Помощь HP InfoTech в Интернете) Эта команда при наличии подключённого Интернета осуществляет переход на домашнюю страницу фирмы HP InfoTech — разработчика CodeVisionAVR. После выбора этой команды будет запущен Internet Explorer и осуществлён пере- ход по адресу: www.hpinfotech.ro. В результате будет открыта домашняя страница фирмы HP InfoTech — разра- ботчика CodeVisionAVR. На сайте фирмы HP InfoTech можно найти много информации по CodeVisionAVR, а также загрузить новые версии этой программы. Команда Help -э> E-Mail HP InfoTech (Помощь Электронная почта HP InfoTech) Эта команда при наличии подключённого Интернета позволяет написать и отправить электронное письмо фирме HP InfoTech — разработчику CodeVisionAVR. После выбора команды будет запущен почтовый клиент, установ- ленный на данном компьютере, с заготовкой электронного письма. Если, например, на компьютере в качестве почтового клиента установлен Outlook Express, то будет выведено окно, показанное на Рис. 2.74. В этом окне уже введен электронный адрес получателя: office@hpinfotech.ro, и тема письма: CodeVisionAVR. Пользователю остаётся только написать текст письма и щёлкнуть по кнопке Отправить. После этого электронное письмо будет отправлено разра- ботчику программы CodeVisionAVR. Рис. 2.74. Заготовка электронного письма в Outlook Express. Команда Help About CodeVisionAVR (Помощь -4> 0 CodeVisionAVR) Эта команда выводит информацию о программе CodeVisionAVR. После выбора этой команды появится окно About CodeVisionAVR (О CodeVisionAVR), показанное на Рис. 2.75.
2.2. Панель инструментов 71 About CodeVisionAVR Code Visio nA VR C Compiler, Integrated Development Environment, HP InfoTacti Automatic Program Generator and In-System Programmer for the Atmel AVR Family of Microcontrollers Version: 1.24.1 c S tandard © Copyright 1998-2004 Pavel Haiduc, HP InfoTech s.r.l. http://www.hpinfotech.ro Licensed to: ШЦЬМИ Serial number: П/OK I Рис. 2.75. Окно About CodeVisionAVR (О CodeVisionAVR). В этом окне выводится краткая информация о программном продукте CodeVisionAVR и номер установленной версии, а также информация о владельце лицензии и серийный номер для данного компьютера. 2.2. Панель инструментов Панель инструментов расположена под строкой меню. Эта панель состоит из кнопок с рисунками (Рис. 2.76). Каждой кнопке соот- ветствует команда, а рисунок на этой кнопке передаёт значение команды. Боль- шинство кнопок дублирует наиболее часто употребляемые команды, доступные в меню. Для вызова команды, связанной с кнопкой, следует щёлкнуть мышью по этой кнопке. Й w & м&| IOI&I Рис. 2.76. Панель инструментов. Если навести указатель мыши на кнопку, рядом появится рамка с названием команды. С помощью кнопок на панели инструментов вызов той или иной команды осуществляется значительно быстрее, чем через меню. В Табл. 2.11 даётся описа- ние кнопок панели инструментов. Описание соответствующих команд см. в главе Строка меню.
72 Глава 2. Интегрированная среда разработки Таблица 2.11. Кнопки панели инструментов Кнопка Соответствующая команда Действие й Settings -> General Show/Hide Navigator (Настройки-» Общие Показать/скрыть навигатор) Показывает или скрывает окно Navigator (Навигатор) File -> New (Файл -> Новый) Позволяет создать новый исходный файл или новый проект б File -> Open (Файл -+ Открыть) Позволяет открыть ранее созданный файл или проект 1 File -» Save (Файл -> Сохранить) Сохраняет файл в активном окне подтем же именем Fie -> Print (Файл -> Печать) Печатает файл, находящийся в активном окне ю Edit^> Undo (Правка -> Отменить изменение) Отменяет изменения в редактируемом файле о- Edit-> Redo (Правка Восстановить изменение) Возвращает отмененные изменения в редактируемом файле х| Edit -> Cut (Правка -> Вырезать) Копирует выделенный фрагмент в буфер обмена и удаляет его из редактируемого файла Edit—> Сору (Правка -» Копировать) Копирует выделенный фрагмент в буфер обмена е Edit —> Paste (Правка -> Вставить) Вставляет содержимое буфера обмена в редактируемый файл Edit^> Find (Правка —> Найти) Осуществляет поиск фрагмента в редактируемом файле mJ Edit^> Replace (Правка -> Заменить) Заменяет один фрагмент в редактируемом файле на другой д| Project-> Compile (Проект -> Компилировать) Компилирует проект Project-^ Make (Проект —> Построить) Осуществляет построение проекта о Project -> Stop Compilation (Проект -> Остановить компиляцию) Останавливает процесс компиляции 0| Projects Information (Проект -» Информация) Выводит окно с информацией й Projects Configure (Проект —> Конфигурировать) Позволяет настроить конфигурацию проекта
2.3. Строка состояния 73 (продолжение) Кнопка Соответствующая команда Действие Tools -> CodeWizardAVR (Инструменты —> CodeWizardAVR) Запускает автоматический генератор программ CodeWizardAVR *1 Tools Debugger (Инструменты -> Отладчик) Запускает отладчик Tools -> Chip Programmer (Инструменты -» Программатор чипов) Запускает программатор для чипов AVR ж. Tools -» Terminal (Инструменты -> Терминал) Запускает терминал последовательной связи ж Windows Tile Horizontal (Окна -> Горизонтально) Располагает окна горизонтально ни Windows^ Tile Vertical (Окна -> Вертикально) Располагает окна вертикально ] Windows —> Cascade (Окна —> Каскадом) Располагает окна каскадом ±1 Help Help Topics (Помощь -> Темы помощи) Выводит содержание помощи 2.3. Строка состояния Строка состояния предназначена для вывода информации о текущей работе программы. Она располагается в самом низу окна программы (Рис. 2.77). Рис. 2.77. Строка состояния.
74 Глава 2. Интегрированная среда разработки Эта строка имеет четыре зоны. В первой зоне (1) выводятся координаты теку- щего положения курсора в активном окне — строка : столбец. Во второй зоне (2) в случае внесения изменений в файл, находящийся в ак- тивном окне, появляется слово Modified (Изменён) (Рис. 2.78). а) б) Рис. 2.78. Строка состояния до (а) и после (б) внесения изменений в файл.
2.4. Область редактирования 75 В третьей зоне (3) выводится текущий режим редактирования: Insert (Вставка) или Overwrite (Замена). Переключение режимов осуществляется на клавиатуре компьютера клавишей <Insert>. В четвертой зоне (4) выводится информация о текущей операции, которую выполняет CodeVisionAVR в процессе компиляции или построения проекта. 2.4. Область редактирования Область редактирования предназначена для просмотра и редактирования файлов. Область редактирования использует отдельное окно редактора для каждого открытого файла. Эти окна можно различными способами упорядочить (см. Ме- ню Windows (Окна)). Область редактирования можно расширить на всё окно программы (Рис. 2.79), закрыв окна Navigator (Навигатор) и Messages (Сообщения) (см. Ко- манда Settings —> General (Настройки —> Общие)). Рис. 2.79. Область редактирования занимает всё окно программы. Область редактирования закрыть нельзя! 2.5. Окно Navigator (Навигатор) Окно Navigator (Навигатор) позволяет легко отображать и/или открывать файлы. Пока не открыт ни один файл или проект, окно Navigator (Навигатор) выгля- дит так, как показано на Рис. 2.80.
76 Глава 2, Интегрированная среда разработки Рис. 2.80. Окно Navigator (Навигатор). При открытии или создании проекта или файла в окне Navigator (Навигатор) появляются названия проекта и файлов. Все файлы, включённые в проект, отображаются в ветви Project (Проект) (Рис. 2.81). Щёлкнув по имени файла, в окне Navigator (Навигатор) можно раз- вернуть (если он до этого был свёрнут) или открыть соответствующий файл. Если такой файл закрыть, то его название останется в окне Navigator (Навигатор). Рис. 2.81. Отображение в окне Navigator (Навигатор) файлов, включённых в проект. Все файлы, находящиеся в области редактирования и НЕ включённые в про- ект, отображаются в окне Navigator (Навигатор) в ветви Other Files (Прочие фай- лы) (Рис. 2.82). Если такой файл закрыть, то он исчезнет и в окне Navigator (На- вигатор).
2.5. Окно Navigator (Навигатор) 77 CodeVisionAVR - led.prj File Edit Project Tools Settings Windows Help Navigator Ml^lftbMltAl llttlolф|Щ »1*1<1»Г 1Д1Ч Tl CodeVisionAVR Ё 0 Project: led ® Notes □ led.c led. asm t 11 \AVRTook\CodeVision\Ex<unples\Le<l\led.c 11 /* Moving LED example к D:\AVRTools\CodeVision\Examples_RUS\LedUed.asm Messages | 1j ?CnripViRi rm АУР C Compiler V1 - 2 2! ; (C) Copyright 1998-2004 Pavel Haiduc, HP Inf 3[ ; http://ww. hpinfotech. ro 4 ;e-mail:officefihpinfotech.ro 4:7 Insert Puc. 2.82. Отображение в окне Navigator (Навигатор) файлов, не включённых в проект. После процессов Compile (Компилировать) или Маке (Построить) в окне Navigator (Навигатор) в ветви Project (Проект) отобразится список подключённых (директивой #include) файлов (ветвь Included Files), глобальных переменных (ветвь Global Variables) и объявленных функций (ветвь Global Variables) в каждом скомпилированном исходном файле Си (Рис. 2.83). Рис. 2.83. Окно Navigator (Навигатор) после процессов Compile (Компилировать) или Маке (Построить).
78 Глава 2, Интегрированная среда разработки Если в окне Navigator (Навигатор) щёлкнуть по названию подключённого файла, то он откроется в области редактирования. Если в окне Navigator (Навига- тор) щёлкнуть по названию глобальной переменной, то в соответствующем ис- ходном файле Си будет выделено имя соответствующей переменной (Рис. 2.84). Рис. 2.84. Выделение глобальной переменной. Если в окне Navigator (Навигатор) щёлкнуть по названию функции, то в соот- ветствующем исходном файле Си будет выделено имя соответствующей функ- ции, а также объявление функции (Рис. 2.85). Рис. 2.85. Выделение функции.
2.5. Окно Navigator (Навигатор) 79 Если в процессе компиляции или построения проекта появились ошибки или предупреждения, то они также будут отображены в окне Navigator (Навигатор). Щёлкнув на ошибке или предупреждении, можно выделить в соответствующем файле строку, содержащую эту ошибку или вызвавшую соответствующее предуп- реждение (см. Компиляция проекта). Ветви дерева в окне Navigator (Навигатор) можно развернуть или свернуть, щёлкая соответ- ственно по кнопкам Ы или S (Рис. 2.86). Щёлкнув правой кнопкой мыши в окне Navigator (Навигатор), можно открыть выпадаю- щее меню (Рис. 2.87). Это меню содержит следующие пункты: • Open (Открыть) — позволяет открыть файл (см. Команда File Open (Файл -> От- крыть))\ • Save (Сохранить) — позволяет сохранить те- кущий редактируемый файл (см. Команда File Save (Файл Сохранить))', • Save All (Сохранить всё) — позволяет сохра- нить все открытые файлы (см. Команда File Рис. 2.86. Сворачивание и раз- ворачивание ветвей дерева в окне Navigator (Навигатор). -» Save All (Файл Сохранить всё))', • Close Current File (Закрыть текущий файл) — позволяет закрыть файл, нахо- дящийся в активном окне редактора (см. Команда File —> Close (Файл За- крыть)), • Close Project (Закрыть проект) — позволяет закрыть проект (см. Команда File Close Project (Файл -> Закрыть проект)) ', Рис. 2.87. Выпадающее меню в окне Navigator (Навигатор).
80 Глава 2. Интегрированная среда разработки • Expand Files Branches (Разворачивать ветви файлов) — если этот пункт выбран (поставлена галочка), то ветви файлов будут показаны развёрну- тыми; • Autoexpand Errors Branches (Автоматически разворачивать ветви ошибок) — если этот пункт выбран (поставлена галочка), то ветви ошибок будут разво- рачиваться автоматически; • Autoexpand Warnings Branches (Автоматически разворачивать ветви преду- преждений) — если этот пункт выбран (поставлена галочка), то ветви пре- дупреждений будут разворачиваться автоматически. Окно Navigator (Навигатор) позволяет также открыть файл Notes (Примеча- ния). Пока проект не скомпилирован (Compile) или не построен (Маке), эта ко- манда (и кнопка на панели инструментов) не активна. Для того чтобы вывести окно Information (Информация), следует выбрать ко- манду Project -> Information (Проект -> Информация) или щёлкнуть по соответс- твующей кнопке на панели инструментов (см. Команда Project -> Notes (Проект -> Примечания)). Кроме того, в окне Navigator (Навигатор) отображаются результаты поиска (см. Команда Edit Find in Files (Правка -> Найти в файлах)). 2.6. Окно Messages (Сообщения) В окне Messages (Сообщения) выводятся различные сообщения об ошибках и предупреждения. Если в процессе компиляции или построения проекта появились ошибки или предупреждения, то они будут отображены в окне Messages (Сообщения). Щёлк- нув на ошибке или предупреждении, можно выделить в соответствующем файле строку, содержащую эту ошибку или вызвавшую соответствующее предупрежде- ние (см. Компиляция проекта. Кроме того, в окне Messages (Сообщения) отображаются результаты поиска (см. Команда Edit -> Find in Files (Правка Найти в файлах)). 2.7. Экспорт лицензии на другой компьютер CodeVisionAVR представляет закрытую лицензированную компьютерную систему. Это означает, что после приобретения программы пользователь получает от автора лицензионный файл, который характерен для конкретного компьютера пользователя. Это не позволит использовать программное обеспечение на другом компьютере, пока на этот компьютер не будет экспортирована лицензия. После экспорта лицензии программа на первом компьютере будет дезактивирована и CodeVisionAVR можно будет запускать только на втором компьютере. Лицензию всегда можно будет экспортировать на первый компьютер, но после этого лицен- зия на втором компьютере будет дезактивирована. Благодаря такой процедуре од- новременно работать с CodeVisionAVR может только один пользователь. Чтобы экспортировать лицензию с компьютера #1 на компьютер #2, необходимо сде- лать следующее:
2.7. Экспорт лицензии на другой компьютер 81 • инсталлировать CodeVisionAVR на компьютер #2; • запустить CodeVisionAVR на компьютере #2, при этом будет показан спе- цифический серийный номер (Рис. 2.88); CodeVisionAVR License Рис. 2.88. Специфический серийный номер. • запустить CodeVisionAVR на компьютере #1 и выбрать соответствующую команду (см. Команда Help -> Export License (Помощь Экспорт лицензии)). При этом откроется диалоговое окно Export License (Экспорт лицензии) (Рис. 2.89); Рис. 2.89. Диалоговое окно Export License (Экспорт лицензии). • ввести серийный номер из компьютера #2 в редактируемое окошко Serial # of the destination computer (Серийный номер данного компьютера); • щёлкнуть по кнопке Export (Экспорт). Если щёлкнуть по кнопке Cancel (Отменить), то экспорт будет отменён; • после этого будет подсказано, куда сохранить новый лицензионный файл для компьютера #2. Обычно можно выбрать дискету в накопителе А: (Рис. 2.90);
82 Глава 2. Интегрированная среда разработки Рис, 2.90. Выбор носителя для лицензионного файла. • щёлкнуть по кнопке Сохранить. После того как новый лицензионный файл успешно будет скопирован на дискету, CodeVisionAVR в компьютере #1 прекратит свою работу; • установить дискету с лицензионным файлом в накопитель А компьютера #2 и щёлкнуть по кнопке Import (Импорт). После этого лицензионная пе- редача будет завершена, и CodeVisionAVR будет работать только на ком- пьютере #2. CodeVisionAVR License Your Serial Number is: ЯВИ * ЩИ- 1 ' and it has been copied to the clipboard. Tо purchase a license, please send this Serial Number to HP InfoTech s.r.l. at: office@hpinfotech.ro If you allready purchased the program, then you must Import the license from a valid CodeVisionAVR installation Import j[ X Cancel Puc. 2.91. Импортирование лицензии. Обратите внимание, что после процедуры экспорта серийный номер ком- пьютера #1 изменится. В этой ситуации, если импортировать лицензию на этот компьютер, необходимо вводить этот новый серийный номер, а не старый.
ГЛАВА РАБОТА В IDE CODEVISIONAVR Используя IDE CodeVisionAVR, можно просмотреть и редактировать любой текстовый файл, любой файл, используемый или произведённый компилятором Си или ассемблером, а также работать с проектами. Так как составной частью любого проекта является файл, рассмотрим сначала работу с файлами. 3.1. Работа с файлами Операции, возможные при работе с файлами в CodeVisionAVR, и способы их реализации приведены в Табл. 3.1. Таблица 3.1. Операции с файлами и способы их реализации Операция Соответствующая команда Кнопка на панели инструментов Раздел данной книги Стр. Создание нового файла File ->New (Файл -> Новый) а] 2.1.1 18 Открытие существующего файла File —> Open (Файл —> Открыть) L 2.1.1 21 Открытие файла, который открывался ранее File -> Reopen (Файл —> Открыть заново) 2.1.1 22 Редактирование файла 3.1.1 82 Сохранение файла File ->Save (Файл -> Сохранить) 2.1.1 23 Переименование файла File Save As... (Файл -> Сохранить как...) 2.1.1 23 Сохранение всех открытых файлов File -> Save All (Файл -> Сохранить все) 2.1.1 25 Печать файла File -> Print (Файл -> Печать) 2.1.1 28 Закрытие файла File Close (Файл -» Закрыть) 2.1.1 25
84 Глава 3. Работа в IDE Code Vision A VR 3.1.1. Редактирование файла Открытый ранее в области редактирования или вновь созданный файл можно отредактировать в окне редактора, используя соответствующие команды (см. Ме- ню Edit (Правка)). Эти команды можно вызывать из строки меню, а можно щёлк- нуть правой кнопкой мыши в окне редактора и воспользоваться выпадающим ме- ню, которое совпадает с меню Edit (Правка) (Рис. 3.1). Unindent Block Find Find Next Find in Files Replace Toggle Bookmark ► Jump to Bookmark ► Goto Line Match Braces Рис. 3.1. Выпадающее меню Edit (Правка) в окне редактора. Кроме того, при редактировании можно воспользоваться «горячими» клави- шами, приведёнными в Табл. 3.2. Таблица 3.2. «Горячие» клавиши редактирования Комбинация клавиш Действие Перемещение курсора Стрелка Т Перемещает курсор на одну строку вверх Стрелка i Перемещает курсор на одну строку вниз Стрелка <— Перемещает курсор на один символ влево Стрелка —> Перемещает курсор на один символ вправо
3.1. Работа с файлами 85 (продолжение) Комбинация клавиш Действие Ctrl + <- Перемещает курсор на одно слово влево Ctrl + -> Перемещает курсор на одно слово вправо Ноте (Домой) Перемещает курсор в начало текущей текстовой строки Ctrl + Ноте Перемещает курсор в начало файла End (Конец) Перемещает курсор в конец текущей текстовой строки Ctrl + End Перемещает курсор в конец файла Page Up Перемещает курсор на одну страницу вверх Page Down Перемещает курсор на одну страницу вниз Ctrl + Page Up Перемещает курсор в начало предыдущей страницы Ctrl + Page Down Перемещает курсор в начало следующей страницы Выделение текста Shift +1 Выделяет текст от те кущей позиции курсора до такой же позиции на строке выше Shift +1 Выделяет текст от текущей позиции курсора до такой же позиции на строке ниже Shift+ <r- Выделяет один символ слева от курсора Shift + -> Выделяет один символ справа от курсора Shift + Ctrl + <— Выделяет одно слово слева от курсора Shift + Ctrl + —> Выделяет одно слово справа от курсора Shift + Home Выделяет текст от текущей позиции курсора до начала текущей текстовой строки Shift + Ctrl + Home Выделяет текст от текущей позиции курсора до начала файла Shift + End Выделяет текст от текущей позиции курсора до конца текущей текстовой строки Shift + Ctrl + End Выделяет текст от текущей позиции курсора до конца файла Shift + Page Up Выделяет текст от текущей позиции курсора до начала экрана Shift + Page Down Выделяет текст от текущей позиции курсора до конца экрана Shift + Ctrl + Page Up Выделяет текст от текущей позиции курсора до начала предыдущей страницы Shift + Ctrl + Page Down Выделяет текст от текущей позиции курсора до конца предыдущей страницы Редактирование Tab (Табуляция) Вставляет определенное количество пробелов (см. Команда Settings -> Editor {Настройки -> Редактор)) Backspace (Забой) Удаляет один символ слева от курсора Delete (Удалить) Удаляет один символ справа от курсора Ctrl+Backspace Удаляет одно слово слева от курсора Ctrl+Del Удаляет одно слово справа от курсора Ctrl+Y Удаляет строку текста, в которой находится курсор Навигация Ctrl + Tab Последовательный переход из одного окна редактора в другое в прямом направлении Shift + Ctrl + Tab Последовательный переход из одного окна редактора в другое в обратном направлении
86 Глава 3. Работа в IDE Code VisionA VR Остальные «горячие» клавиши приведены при описании соответствующих команд (см. Меню Edit (Правка)). Фрагменты текста также можно выбрать с помощью мышью. Для этого уста- новите курсор в нужное место и, не отпуская левую кнопку мыши, перетащите курсор в конец выделяемого фрагмента, после чего отпустите кнопку мыши. Можно выбрать слово, дважды щёлкнув по нему мышью. Выделяя и перетаскивая с помощью мыши, можно перемещать фрагменты текста. Если при перетаскивании удерживать нажатой клавишу <Ctrl>, то этот фрагмент текста будет скопирован в новое место. В окне редактирования можно перемещаться, воспользовавшись колесом мы- ши. 3.2. Работа с проектами Проект группирует исходный файл(ы) и настройки компилятора, которые ис- пользуются для построения конкретной программы. Операции, возможные при работе с проектом в CodeVisionAVR, и способы их реализации приведены в Табл. 3.3. Таблица 3.3. Операции с проектами и способы их реализации Операция Соответствующая команда Кнопка на панели инструментов Раздел книги Создание нового проекта File —> New (Файл —> Новый) о| 2.1.1 Открытие существующего проекта File -» Open (Файл -> Открыть) и 2.1.1 Открытие заново проекта, который открывался ранее File -> Reopen (Файл -» Открыть заново) 2.1.1 Сохранение проекта File -> Save All (Файл -> Сохранить всё) 2.1.1 Добавление примечания или комментария в проект Project ->Notes (Проект -> Примечания) 2.1.3 Конфигурирование проекта Project —> Configure (Проект -> Конфигурация) SI 2.1.3 Компилирование проекта Project ->Compile (Проект -> Компилировать) я 2.1.3 Построение проекта Project ->Make (Проект -> Построить) 2.1.3 Отладка программы Tools -> Debugger (Инструменты Отладчик) 2.1.4 Запись программы в чип AVR Tools -> Chip Programmer (Инструменты -> Программатор чипов) »1 2.1.4 Закрытие проекта File -> Close Project (Файл -> Закрыть проект) 2.1.1
3.2. Работа с проектами 87 Одновременно можно работать только с одним проектом. При открытии нового проекта текущий будет закрыт автоматически. 3.2.1. Конфигурирование проекта Сконфигурировать проект можно в любой момент работы над ним (см. Ко- манда Project -> Configure (Проект Конфигурировать)). Диалоговое окно Configure Project (Конфигурация проекта) имеет три заклад- ки: Files (Файлы), С Compiler (Компилятор Си), After Make (После построения) (Рис. 3.2). ч Configure Project led.prj Files | C Compiler | After Make | S -Qi dAavrtools\codevision\examples_ru$\l Рис. 3.2. Закладки диалогового окна Configure Project (Конфигурация проекта). Каждая закладка позволяет настроить определённые опции проекта. Закладка Files (Файлы) Закладка Files (Файлы) позволяет добавить или удалить файлы из проекта (Рис. 3.3). Рис. 3.3. Закладка Files (Файлы) диалогового окна Configure Project (Конфигурация проекта).
88 Глава 3. Работа в IDE Code VisionA VR Чтобы добавить файл к проекту, следует щёлкнуть по кнопке Add (Добавить). При этом откроется диалоговое окно Add File То Project (Добавить файл к проек- ту). В этом окне следует выбрать необходимый файл и щёлкнуть по кнопке От- крыть (Рис. 3.4). После этого файл будет добавлен в проект (Рис. 3.5). Рис. 3.4. Выбор файла в окне Add File То Project (Добавить файл к проекту). ч Configure Project led.prj Files | С Compiler j After Make | d: \avr tools\codevisiorAexamples_f usMedMed prj И 0: \AVR T ools\CodeVision\E xamples\LedMed. i П D:\AVRTools\CodeVi$ion\Example$\Le4$ Рис. 3.5. Файл led_.c добавлен в проект. Первый добавленный к проекту файл — основной (main) файл проекта. В этом файле ОБЯЗАТЕЛЬНО должна присутствовать функция main. Остальные файлы, добавленные к проекту, автоматически будут связаны с основным (main) при построении (Маке) проекта (см. Построение проекта). Чтобы добавить в проект сразу несколько файлов, можно или удерживать кла- вишу <Ctrl> при выборе файлов в диалоговом окне Add File to Project (Добавить файл в проект), или выделить требуемые файлы при помощи мыши, удерживая нажатой левую кнопку. Если после добавления файлов к проекту на закладке Files (Файлы) диалого- вого окна Configure Project (Конфигурация проекта) щёлкнуть по кнопке ОК, то в области редактирования будут открыты все файлы проекта (Рис. 3.6).
3.2. Работа с проектами 89 Рис. 3.6. В области редактирования открыты все файлы проекта. Чтобы удалить файл из проекта, следует выбрать требуемый файл на закладке Files (Файлы) диалогового окна Configure Project (Конфигурация проекта) и щёл- кнуть по кнопке Remove (Удалить) (Рис. 3.7). Рис. 3.7. Удаление файла led_.c из проекта.
90 Глава 3. Работав IDECodeVisionAVR При создании проекта с несколькими файлами необходимо соблюдать следу- ющие правила: • к списку файлов проекта могут добавляться только файлы с расширением .с; • отпадает необходимость в директиве #include для файлов с расширени- ем .с из списка файлов проекта, так как они будут подключены автома- тически; • определения типа данных и объявления функций должны быть размещены в заголовочных файлах с расширением .h, которые должны быть подключе- ны в файлах с расширением .с с помощью директивы #include там, где это необходимо; • объявления глобальных переменных должны быть установлены в файлах с расширением .с там, где необходимо; • не следует объявлять нестатические глобальные переменные в заголовоч- ных файлах с расширением .h, поскольку, если эти файлы будут подключе- ны с помощью директивы #include неоднократно, компилятор выдаст ошибки о повторных определениях переменных. Закладка С Compiler (Компилятор Си) Закладка С Compiler (Компилятор Си) позволяет установить опции компиля- тора Си для открытого в настоящий момент проекта. Чтобы выбрать эту закладку в диалоговом окне Configure Project (Конфигура- ция проекта), следует щёлкнуть по ней мышью (Рис. 3.8). Рис. 3.8. Выбор закладки С Compiler (Компилятор Си) в диалоговом окне Configure Project (Конфигурация проекта). Закладка С Compiler (Компилятор Си) в свою очередь имеет ещё три закладки: Code Generation (Генерация кода), Globally #define (Глобальные определения) и Paths (Пути). На закладке Code Generation (Генерация кода) (Рис. 3.9) можно задать следую- щие опции компилятора: • Chip (Чип) — в этом окошке в выпадающем списке можно выбрать микро- контроллер AVR, для которого разрабатывается проект. В соответствии с заданным микроконтроллером компилятор проверяет допустимость тех или иных операторов, наличие соответствующей периферии и т. д. CodeVisionAVR версии 1.24. lx Standart поддерживает следующие типы мик- роконтроллеров:
3.2. Работа с проектами 91 Atiny 13; Atiny2313V; AT90S2323; AT90S2343; Atiny22; Atiny26; AT90LS2323; AT90LS2343; Atiny22L; Atiny26L; AT90S2333; AT90S4414; Atiny2313; AT90S2313; AT90LS2333; AT90S4433; AT90LS4433; ATmegal61; ATmega 169V; ATmega8L; AT90S4434; ATmegal61L; ATmega32; ATmega8515; AT90LS4434; ATmegal62; ATmega32L; ATmega8515L; AT90S8515; ATmegal62L; ATmega323; ATmega8535; AT90S8534; ATmegal62U; ATmega323L; ATmega8535L; AT90S8535; ATmegal62V; ATmega48; ATmega88; ATmegalO3; ATmegal63; ATmega48V; ATmega88V; ATmegalO3L; ATmegal63L; ATmega603; AT43USB355; ATmegal28; ATmegal68; ATmega603L; AT76C711; ATmegal28L; ATmegal68V; ATmega64; AT86RF401; ATmegal6; ATmegal69; ATmega64L; AT94K05; ATmegal6L; ATmegal69L; ATmega8; AT94K. Рис. 3.9. Закладка Code Generation (Генерация кода) на закладке С Compiler (Компилятор Си) в диалоговом окне Configure Project (Конфигурация проекта).
92 Глава 3. Работа в IDE Code VisionA VR В выпадающем списке окошка Chip (Чип) можно выбрать требуемый чип. • Clock (Тактовая частота) — в этом окошке следует ввести значение тактовой частоты в мегагерцах. Это значение CodeVisionAVR будет использовать для временных вычислений, которые нужны для функций задержки, функций протокола 1-Wire и функций температурных датчиков DS1820/DS18S20 от Dallas Semiconductor (см. Использование библиотечных функций). Значение тактовой частоты можно ввести непосредственно в окошке Clock (Тактовая частота) либо воспользоваться стрелками, расположенными справа от этого окошка. • Memory Model (Модель памяти) — в этом поле можно выбрать необходи- мую модель памяти: Tiny (Очень маленькая) или Small (Маленькая). Для улучшения скорости и размера программы следует всегда пытаться ис- пользовать модель памяти Tiny (см. также Указатели). • Optimize for (Оптимизировать по) — в этом поле можно выбрать критерий оптимизации программы: Size (По размеру исполняемого кода) или Speed (По скорости выполнения программы). Эту опцию можно также определить, используя директивы компилятора #pragma opt и #pragma optsize (см. Директива ttpragma opt и Директива #pragma optsize). Как правило, всегда следует выбирать критерий Speed, и, только если про- грамма не помещается в памяти чипа, следует попробовать оптимизиро- вать её по размеру, т. е. выбрать критерий Size. • Program Type (Тип программы) — в этом окошке можно выбрать тип про- граммы: Application (Приложение) или Boot Loader (Загрузчик). Эта опция доступна только для устройств, которые допускают само- программирование. Подробнее читайте описания на эти устройства от про- изводителя (см. Команда Help AVR Data Sheets (Помощь Описания AVR)). Если был выбран тип программы Boot Loader (Загрузчик), то будет доступна дополнительная опция Boot Loader Debugging in AVR Studio (Отлаживать загрузчик в AVR Studio) (Рис. 3.10). Если разрешить эту опцию (поставить галочку), то ком- пилятор сгенерирует вспомогательный код, который позволит отлаживать За- грузчик (Boot Loader) в имитаторе/эмуляторе AVR Studio на исходном уровне. int, width 17 17 8 bit enums!7 Enhanced Core Instructions Automatic Register Allocation Use an External Startup Initialization File nable Warnings k End Markers ✓ QK | X Cancel [ ? Help [ Puc. 3.10. Дополнительная опция Boot Loader Debugging in AVR Studio (Отлаживать загрузчик в AVR Studio).
3.2. Работа с проектами 93 Перед программированием чипа с кодом Boot Loader (Загрузчик) опция Boot Loader Debugging (Загрузчик отладчика) должна быть отключена (галоч- ка снята), а программа заново перекомпилирована. Начинающим следует выбирать тип программы Application (Приложение). • (s)printf features ((s)printf характеристики) — эта опция позволяет выбрать версии стандартных функций вывода Си printf и sprintf, которые будут включены в проект (см. Стандартные функции ввода/вывода). Здесь в выпа- дающем списке (Рис. 3.11) доступны характеристики, приведённые в Табл. 3.4. Program Туре: I ini width | Boot Loader 128w ▼] js)printf features^ ini width long, width long, width, precision float, width, precision f* *7 8 bit enumsF/ Enhanced Core Instructions p Automatic Register Allocation F Use an External Startup Initialization File P Enable Warnings F Stack End Markers File Output Format(s): |cOF ROM HEX EEFjJ F Use the Terminal I/O in AVR Studio F Boot Loader Debugging in AVR Studio ✓ QK | X Cancel | ? Help | Puc. 3.11. Выпадающий список опции (s)printf features ((s)printf характеристики). Чем больше характеристик выбрано для функций printf и sprintf, тем боль- ше размер сгенерированного кода. • (s)scanf features (характеристики (s)scanf) — эта опция позволяет выбрать, какие версии стандартных функций ввода Си scanf и sscanf будут включены в проект (см. Стандартные функции ввода/вывода). Здесь в выпадающем списке (Рис. 3.12) доступны характеристики, приведенные в Табл. 3.5. I Hiuth<.»eu^ .... i* char isijisigned P BbitenumsP Enhanced Core Instructions p Automatic Register Allocation F Use an External Startup Initialization File P Enable Warnings F Stack End Markers File Output Formal(s): |COF ROM’HEX EEF Г Use the Terminal J/0 in AVR Studio Program Type: [Application (s)printf features: [ini width " fsjscanf feature? int, width long, width Puc. 3.12. Выпадающий список опции (s)scanf features (характеристики (s)scanf).
94 Глава 3. Работа в IDE Code VisionA VR Таблица 3.4, Характеристики функций printf и sprintf Характеристика Описание int Поддерживаются следующие символы преобразования типа: 'с', 's', 'р', Т, '(Г, 'и', 'х', 'X','%'; не поддерживается спецификация ширины и точности, поддерживаются только флаги '+' и не поддерживается никакое входное изменение размерности (типа) int, width Поддерживаются следующие символы преобразования типа: 'с', 's', 'р', Т, '(Г, 'и', 'х', 'X,'%'; поддерживается спецификация ширины; спецификация точности не поддерживается, поддерживаются только флаги '+','О' и ''; не поддерживается никакое входное изменение размерности (типа) long, width Поддерживаются следующие символы преобразования типа: 'с', 's', 'р', Т, '(Г, 'и', 'х', 'X','%'; поддерживается спецификация ширины, спецификация точности не поддерживается; поддерживаются только флаги '+','О' и ''; поддерживается только входное изменение размерности (типа) 'Г long, width, precision Поддерживаются следующие символы преобразования типа: 'с', 's', р', Т, 'd', 'и', 'х', 'X, ’%'; поддерживается спецификация ширины и точности; поддерживаются только флаги '+','О' и ''; поддерживается только входное изменение размерности 'Г float, width, precision Поддерживаются следующие символы преобразования типа: 'с', 's', 'р', 'i', 'd', 'и', 'е', 'Е', Т, 'х', X,'%'; поддерживается спецификация ширины и точности, поддерживаются только флаги '+','О' и ''; поддерживается только входное изменение размерности (типа) 'Г Таблица 3.5, Характеристики функций scanf и sscanf Характеристика Описание int, width Поддерживаются следующие символы преобразования типа: 'с', 's', 'i', 'd', 'u', 'x','%', поддерживается спецификация ширины; не поддерживается никакое входное изменение размерности (типа) long, width Поддерживаются следующие символы преобразования типа: 'с', 's', 'Г, 'd*, 'и', 'х','%', поддерживается спецификация ширины; поддерживается только входное изменение размерности (типа) 'Г Чем больше характеристик выбрано для функций scanf и sscanf тем боль- ше размер сгенерированного кода. • SRAM (ОЗУ) — в этом поле задаётся распределение памяти SRAM (ОЗУ) в чипе (Рис. 3.13). Здесь доступны следующие опции: — Data Stack Size (Размер стека данных) — позволяет задать размер стека данных в байтах; — Heap size (Размер кучи) — позволяет задать размер в байтах так называе- мой кучи — области памяти, которая используется для функций дина- мического распределения памяти; — Internal SRAM Size (Размер внутреннего SRAM) — показывает размер в байтах внутреннего SRAM данного чипа. Это значение изменить нельзя! — External SRAM Size (Размер внешнего SRAM) — позволяет задать размер внешнего SRAM (в случае, если к микроконтроллеру подключена вне- шняя SRAM); — External SRAM Wait State (Состояние ожидания внешнего SRAM) — эта опция разрешает ввести состояние ожидания в течение доступа к вне-
3.2. Работа с проектами 95 шнему SRAM. Это полезно при использовании медленных устройств памяти. Если выбрать устройства Atmel типа FPSLIC АТ94К05, АТ94К10, АТ94К20 или АТ94К40, то появится дополнительная опция Program SRAM size (раз- мер программного SRAM) (Рис. 3.14). Эта опция позволяет определить размер программного SRAM в Kwords (Келов) для указанных устройств. * Рис. 3.13. Поле SRAM (ОЗУ). Рис. 3.14. Дополнительная опция Program SRAM size (Размер программного SRAM). • Code Generation (Генерация кода) — в этом поле задаются опции для непос- редственной компиляции (Рис. 3.15). Здесь доступны следующие опции: — Bit Variables size (Размер битовых переменных) — эта опция позволяет задать (выбрать в выпадающем списке) размер битовых переменных, ко- торые размещаются в регистрах R2...R14; — Promote char to int (Переводить символ в целое) — эта опция разрешает переводить операнды из char (символ) в int (целое). Эту опцию можно также определить, используя директиву компилятора #pragma promotechar (см. Директива ttpragmapromotechar). Перевод char (символ) в int (целое) приводит к увеличению размера кода и уменьшает скорость для 8-битных микроконтроллеров AVR;
96 Глава 3. Работа в IDE Code VisionA VR Optimize for. Size C Speed Code Generation Program Type: (Application (s)printf features: I int, width (s)scanf features: I int, width Г Promote chjr to int I*7 char is unsigned 15* SbitenumsF/ Enhanced Core Instructions |7 Automatic R egister Allocation Г* Use an External Startup I realization file I*7 Enable Warnings F Stack End Markers File Output Format(s): jcOF FIOM HEX EEF jrj — Puc. 3.15. Поле Code Generation (Генерация Кода). — char is unsigned (символ является беззнаковым) — если выбрать эту оп- цию, то компилятор по умолчанию будет обрабатывать данные типа char (символ), как 8 битов без знака, в диапазоне 0...255. Если эту опцию не активировать (снять галочку), то компилятор по умолчанию будет обра- батывать данные типа char (символ), как 8 битов со знаком, в диапазоне — 128... 127 (см. также Типы Данных). Эту опцию также можно определить, используя директиву компилятора #pragma uchar (см. Директива ttpragma uchar). Обработка char (символа), как беззнакового, уменьшает размер кода и увеличивает скорость вы- полнения скомпилированной программы; — 8 bit enums (8-битовые перечисления) — если отметить эту опцию, то компилятор будет обрабатывать перечисления, как 8-битовый char (символ) тип данных. Если эту опцию не активировать (снять галочку), то перечисления считаются как 16-битовый int (целый) тип данных, как требует стандарт ANSI. Эту опцию также можно определить, используя директиву компилятора #pragma 8bit_enums (см. Директива ttpragma 8bit_enums). Обработка пере- числений, как тип данных char (символ), уменьшает размер кода и уве- личивает скорость выполнения скомпилированной программы; — Enhanced Core Instructions (Расширенные инструкции ядра) — эта опция разрешает или запрещает генерацию расширенных инструкций ядра для ATmegal28, ATmegal6, ATmegal61, ATmegal62, ATmegal63, ATmega32, ATmega323, ATmega64, ATmega8 и устройств AT94K FPSLIC. Эта опция доступна только для этих устройств; — Automatic Register Allocation (Автоматическое распределение регистров) — эта опция разрешает автоматически распределять char (символьные) и int (целые) глобальные переменные в регистры, не использованные для битовых переменных, в диапазоне R2...R14; — Use an External Startup Initialization File (Использовать внешний запуска- ющий файл инициализации) — эта опция разрешает использовать вне- шний запускающий файл инициализации;
3.2. Работа с проектами 97 — Enable Warnings (Разрешить предупреждения) — эта опция разрешает или запрещает генерацию предупреждающих сообщений в течение ком- пиляции. Эту опцию также можно определить, используя директиву компилятора #pragma warn (см. Директива ttpragma warn). Для начинающих эту опцию рекомендуется разрешить; — Stack End Markers (Отмечать конец стека) — эта опция предназначена только для отладки. Если эта опция выбрана, то компилятор установит строку DSTACKEND в конце Data Stack (Стека данных) или, при нали- чии аппаратного стека, строку HSTACKEND в области Hardware Stack (Аппаратного стека). При отладке программы в отладчике AVR Studio можно увидеть, если эти строки были переписаны, и, следовательно, из- менить Data Stack Size (Размер стека данных). Если программа работает правильно, можно запретить установку этих строк для уменьшения раз- мера получаемого кода; — File Output Format(s) (Формат(ы) выходных файлов) — эта опция позво- ляет задать (выбрать в выпадающем списке) форматы файлов, которые будет генерировать компилятор (Рис. 3.16). Program Type: | Application (s)printf features: | int, width fsjscanf features: [int width I Huiim-i® и._ -.. char is ui ыулес! I* 8 bit egumsP Enhanced Core Instructions I Automatic Register Allocation Г Use an External Startup Initialization File P Enable Warnings П ,5tack.£nd.Marker$ Ale Output Formatjs^QBJ ROM HEX EEFjjT (OBJ ROM HEX EEP^ COFROMHEXEEP ✓ QK | X Cancel [ ? Help | —.......................................u........................................................................... Puc. 3.16. Выпадающий список опции File Output Format(s) (Формат(ы) выходных файлов). Здесь доступны два набора файлов: > COF (требуется отладчику Atmel AVR Studio), ROM, Intel HEX и EEP (требуются In-System Programmer (Внутрисхемному программатору)); > OBJ (общий для Atmel), ROM, Intel HEX и EEP (требуются In-System Programmer (Внутрисхемному программатору)). При выборе набора файлов с форматом COF появится дополнительная опция Use the Terminal I/O in AVR Studio (Использовать терминал ввода/вывода в AVR Studio) (Рис. 3.17). Если выбрать эту опцию, то будет сгенерирована специальная отладочная ин- формация для того, чтобы в AVR Studio версии 3 использовать Terminal I/O (Терми- нал ввода/вывода) для связи с UART симулируемого чипа AVR. Если эта опция выбрана, то код UART или USART будет работать неправильно в реальном чипе AVR. Эта опция предназначена ТОЛЬКО для отладочных целей.
98 Глава 3. Работа в IDE Code VisionA VR Рис. 3.17. Дополнительная опция Use the Terminal I/O in AVR Studio (Использовать терминал ввода/вывода в AVR Studio). При разработке нового проекта начинающим рекомендуется на закладке С Compiler (Компилятор Си) установить только тип чипа и значение так- товой частоты. Остальные настройки рекомендуется оставить уста- новленными по умолчанию. На закладке Globally #define (Глобальные определения) можно ввести глобаль- ные определения, которые будут видны во всех файлах проекта (Рис .3.18). Рис. 3.18. Закладка Globally #define (Глобальные определения) на закладке С Compiler (Компилятор Си) диалогового окна Configure Project (Конфигурация проекта).
3.2. Работа с проектами 99 Например, если ввести в этом окне строку xtal 3686400, как показано на Рис. 3.18, то это будет эквивалентно помещению в каждый файл проекта строки определения: #define xtal 3686400 Каждое определение, вводимое на закладке Globally # define (Глобальные оп- ределения), должно начинаться с новой строки. Закладка Paths (Пути) (Рис. 3.19) позволяет определить: • #include path (one per line) — дополнительные пути для подключаемых фай- лов (один на строку); • Library path (one per line) — дополнительные пути для библиотечных фай- лов (один на строку). Рис. 3.19. Закладка Paths (Пути) на закладке С Compiler (Компилятор Си) диалогового окна Configure Project (Конфигурация проекта). После изменений конфигурации проекта следует щёлкнуть по кнопке ОК. Чтобы отказаться от изменения конфигурации проекта, щёлкните по кнопке Cancel (Отменить). При изменении конфигурации проекта можно воспользоваться помощью, щёлкнув по кнопке Help (Помощь).
100 Глава 3. Работа в IDE Code VisionA VR Закладка After Make (После построения) Закладка After Make (После построения) позволяет выбрать действия компи- лятора после построения проекта (см. Построение проекта). Чтобы выбрать эту закладку в диалоговом окне Configure Project (Конфигура- ция проекта), следует щёлкнуть по ней мышью (Рис. 3.20). Рис. 3.20. Выбор закладки After Make (После построения) в диалоговом окне Configure Project (Конфигурация проекта). После выбора этой закладки диалоговое окно Configure Project (Конфигура- ция проекта) будет выглядеть так, как показано на Рис. 3.21. Рис. 3.21. Закладка After Make (После построения) диалогового окна Configure Project (Конфигурация проекта). На этой закладке можно выбрать следующие опции: • Program the Chip (Программировать чип); • Execute User’s Program (Выполнить программу пользователя). Если выбрать опцию Program the Chip (Программировать чип), то после ус-
3.2. Работа с проектами 101 пешной компиляции/ассемблирования программа автоматически будет передана в чип AVR, используя программное и аппаратное обеспечение выбранного программатора (см. Команда Setting? -> Programmer (Настрой- ки Программатор)). При этом автоматически будут выполнены следую- щие операции: — в чипе стирается FLASH- и EEPROM-память; — FLASH- и EEPROM-память проверяются на чистоту; - FLASH -память программируется и проверяется; - EEPROM -память программируется и проверяется; — программируются Fuse- и Lock- биты микроконтроллера. При выборе опции Program the Chip (Запрограммировать чип) вид диа- логового окна Configure Project (Конфигурация проекта) изменится (Рис. 3.22). Рис. 3.22. Вид диалогового окна Configure Project (Конфигурация проекта) при выборе опции Program the Chip (Запрограммировать чип) на закладке Alter Make (После построения). При этом появится дополнительная опция: • Merge data from a .ROM File for FLASH Programming (добавить данные из файла с расширением .гот при программировании FLASH-памяти). При построении (Маке) проекта компилятор создаёт файл с расширением trom, содержимое которого будет запрограммировано во FLASH-память микроконт- роллера. Если выбрать данную опцию, то на закладке After Make (После постро- ения) появится окошко .ROM File Path (Путь к файлу с расширением .гот) (Рис. 3.23).
102 Г<iaea 3. Работа в IDE Code VisionA VR Puc. 3.23. Окно .ROM File Path (Путь к файлу с расширением .гот). В этом окне можно выбрать файл с расширением .гош, содержимое которого будет добавлено к программному буферу FLASH-памяти вместе с содержимым файла с расширением .гош, созданного компилятором после построения (Маке) проекта. Для этого можно ввести путь к требуемому файлу непосредственно в ок- не .ROM File Path (Путь к файлу с расширением .гош) или воспользоваться кноп- кой fel и определить этот путь обычными средствами Windows. Это полезно, например, при добавлении программы загрузчика, скомпилиро- ванной в другом проекте, в прикладную программу, которая будет запрограмми- рована во FLASH-память микроконтроллера. Кроме того, при выборе опции Program the Chip (Запрограммировать чип) также появится поле Chip Programming Options (Опции программирования чипа) (Рис. 3.24). Опции, находящиеся в поле Chip Programming Options (Опции программиро- вания чипа), описаны ниже (см. Запись программы в uunAVR). Если на закладке After Make (После построения) выбрать опцию Execute User’s Program (Выполнить программу пользователя), то после успешной компи- ляции/ассемблирования будет выполнена программа, которую определил поль- зователь. При выборе опции Execute User’s Program (Выполнить программу пользовате- ля) появится кнопка Program Settings (Настройки программы) (Рис. 3.25).Если щёлкнуть по этой кнопке, то появится диалоговое окно User Program Settings (Настройка программы пользователя) (Рис. 3.26).В этом диалоговом окне можно задать следующие параметры программы пользователя: • Program Directory and FileName (Директория программы и имя файла); • Command Line Parameters (Параметры командной строки программы); • Working Directory (Рабочая директория программы). Первый и третий параметры можно ввести непосредственно в соответствую- щие окошки, а можно воспользоваться кнопкой Ж| и определить эти парамет- ры обычными средствами Windows. Все изменения можно сохранить, щёлкнув по кнопке ОК, или отменить, щёл- кнув по кнопке Cancel (Отменить).
3.2. Работа с проектами 103 Рис. 3.24. Поле Chip Programming Options (Опции программирования чипа). Рис. 3.25. Кнопка Program Settings (Настройка программы). Рис. 3.26. Диалоговое окно User Program Settings (Настройка программы пользователя).
104 Глава 3. Работа в IDE Code VisionA VR 3.2.2. Компиляция проекта Для компиляции открытого проекта следует выбрать соответствующую ко- манду (см. Команда Project -> Compile (Проект Компилировать)). При этом бу- дет запущен компилятор Си CodeVisionAVR, который произведёт ассемблерный файл с именем текущего проекта и с расширением .asm. Этот файл можно про- смотреть и при необходимости изменить, открыв его в редакторе. С помощью CodeVisionAVR можно скомпилировать ТОЛЬКО проект. Отде- льный файл скомпилировать нельзя. Процесс компиляции в любой момент можно прервать (см. Команда Projects Stop Compilation (Проект Остановить компиляцию)). После того как компиляция будет завершена, откроется окно Information (Ин- формация) с результатами компиляции (Рис. 3.27). В первых 9 строках этого окна выводится информация о настройках компиля- тора (см. Конфигурирование проекта). Далее выводится информация о количестве скомпилированных строк (здесь — 156), причём суммируется количество строк во всех включённых в про- ект файлах и в файлах используемых библиотек. Рис. 3.27. Окно Information (Информация).
3.2. Работа с проектами 105 В следующих строках выводится следующая информация: • количество ошибок (errors) и предупреждений (warnings) при компиляции; • Bit variables size — размер, занимаемый битовыми переменными; • Data Stack area — область в адресном пространстве, занимаемая стеком данных; • Data Stack size — размер области стека данных, который задаётся при кон- фигурировании проекта (см. Закладка С Compiler (Компилятор Си)); • Estimated Data Stack usage — оценочное использование стека данных; • Global variables area — область в адресном пространстве, занимаемая гло- бальными переменными; • Global variables size — размер области глобальных переменных; • Hardware Stack area — область в адресном пространстве, занимаемая аппа- ратным стеком; • Hardware Stack size — размер области аппаратного стека; • Heap size — размер так называемой кучи — области памяти, которая ис- пользуется для функций динамического распределения памяти; • EEPROM usage — использование EEPROM. Количество возможных ошибок компиляции и/или предупреждений будет указано в окне Information (Информация), как показано на Рис. 3.28. Рис. 3.28. Информация о количестве ошибок компиляции.
106 Глава 3. Работа в IDECodeVisionAVR Сами ошибки компиляции и/или предупреждения будут показаны в окне Messages (Сообщения) (см. Окно Messages (Сообщения)), расположенном под облас- тью редактирования, и в окне Navigator (Навигатор) (см. Окно Navigator (Навигатор)). Если щёлкнуть по наименованию ошибки (или предупреждения) в окне Navigator (Навигатор) или в окне Messages (Сообщения), то в области редактиро- вания в соответствующем файле будет выделена строка, содержащая эту ошибку (или предупреждение) (Рис. 3.29). Рис, 3.29. Отображение ошибок компиляции. Таким образом, команда Project -» Compile (Проект -> Компилировать) ис- пользуется для проверки и устранения ошибок в проекте перед его окончатель- ным построением командой Project-> Make (Проект Построить). 3.2.3. Построение проекта Для построения открытого проекта следует выбрать соответствующую коман- ду (см. Команда Project Маке (Проект Построить)). При этом сначала будет запущен компилятор Си CodeVisionAVR, который произведёт ассемблерный файл с расширением .asm. Этот файл можно просмотреть и при необходимости изменить, открыв его в редакторе (см. Команда File -> Open (Файл —> Открыть)). Процесс компиляции в любой момент можно прервать (см. Команда Project Stop Compilation (Проект -> Остановить компиляцию)). Если не было обнаружено никаких ошибок, то будет запущен ассемблер Atmel AVR AVRASM32, который произведёт выходные файлы, тип которых определён ранее при конфигурировании проекта на закладке С Compiler (Компилятор Си) (см. Закладка С Compiler (Компилятор Си)).
3.2. Работа с проектами 107 После этого откроется окно Information (Информация), показывая результаты построения. Это окно имеет три закладки: Compiler (Компилятор), Assembler (Ас- семблер) и Programmer (Программатор) (Рис. 3.30). i Information Compiler | Assembler | Programmer | Chip: AT90S8515 Memory model: Small Optimize for: Size (s)printf features: int, width (sjscanf features: int, width Promote char to int: No char is unsigned: Yes 8 bit enums: Yes Automatic register allocation: Off 156 line(s) compiled No errors No warnings Bit variables size: 0 byte(s) Data Stack area: 60h to DFh Data Stack size: 128 byte(s) Estimated Data Stack usage: 12 bytefs) Global variables area. EOh to EOh Global variables size: 1 byte(s) Hardware Stack area: Elh to 25Fh Hardware Stack size: 383 byte(s) Heap size: 0 byte(s) Е£ШО.М,шаое.. П Utafc) Щ.Ш». d E EPROM) Program size: 131 words (3,22 of FLASH) I Cancel Puc. 3.30. Окно Information (Информация), закладка Compiler (Компилятор). Информация на закладке Compiler (Компилятор) практически полностью совпадает с информацией, которая выдаётся после выполнения команды Project Compile (Проект -> Компилировать) (см. Рис. 3.27). Единственное различие (выделено красной рамкой) — это дополнительная информация о размере ском- пилированной программы (Program size) и процент использования Flash-памяти (Памяти программ). Количество возможных ошибок компиляции и/или предупреждений будет указано в окне Information (Информация) на закладке Compiler (Компилятор), а сами ошибки компиляции и/или предупреждения будут показаны в окне Messages (Сообщения), расположенном под областью редактирования, и в окне Navigator (Навигатор) точно так же, как при выполнении команды Project Compile (Проект -> Компилировать) (см. Компиляция проекта). Информация на закладке Assembler (Ассемблер) отображает результаты рабо- ты ассемблера (Рис. 3.31). В первых строках выводится информация о файлах, которые создал (Creating) ассемблер.
108 Глава 3. Работа в IDE Code Vision A VR Далее выводится информация о файлах, которые были странслированы ас- семблером (Assembling), а также какие файлы подключены (Including) к ассемб- лерному файлу. Затем приводится следующая информация об использовании программной памяти (Program memory usage): • сколько слов (words) занимает собственно код программы (Code); • сколько слов (words) занимают табличные константы (Constant (dw/db)); • неиспользуемый (Unused) объём памяти; • сколько слов (words) памяти занято всего (Total). В заключение приводится количество ошибок по окончании транслирования (Assembly complete with no errors). Информация на закладке Programmer (Программатор) (Рис. 3.32) отображает значение Счётчика программирований чипа (Chip Programming Counter). Хотя ко- личество перепрограммирований чипов Atmel очень большое, оно всё-таки ко- нечное, и иногда полезно знать, сколько раз ваш чип был перепрограммирован. Рис, 3.31. Окно Information (Информация), закладка Assembler (Ассемблер). Рис. 3.32. Окно Information (Информация), закладка Programmer (Программатор). Значение Счётчика программирований чипа можно устанавливать самостоя- тельно, если известно, например, сколько раз до этого чип был перепрограмми- рован. Для этого следует щёлкнуть по кнопке Set Counter (Установка счётчика), в появившемся диалоговом окне Set Programming Counter (Установка счётчика про-
3.2. Работа с проектами 109 граммирований) (Рис. 3.33) ввести соответствующее значение в окошке New Counter Value (Новое значение счётчика) и щёлкнуть по кнопке ОК. Значение счётчика программировании чипа (Chip Programming Counter) за- поминается для ДАННОГО проекта. В другом проекте это значение мо- жет быть иным. Рис. 3.33. Диалоговое окно Set Programming Counter (Установка счётчика программирований). Кроме того, на закладке Programmer (Программатор) окна Information (Ин- формация) имеется кнопка Program (Программировать). При нажатии на эту кнопку СРАЗУ начнётся автоматическое программирование чипа, разумеется, ес- ли к компьютеру подключены соответствующие аппаратные средства и сделаны их настройки (см. Команда Settings Programmer (Настройки —> Программатор)). Нажатие на кнопку Cancel (Отмена) запретит автоматическое программиро- вание чипа. Закладка Programmer (Программатор) и кнопка Program (Программиро- вать) внизу окна Information (Информация) появятся только в том слу- чае, если при конфигурировании проекта на закладке After Make (После построения) была выбрана опция Program the Chip (Программировать чип) (см. Закладка After Make (После Построения)). 3.2.4. Отладка программы Для отладки программ в IDE CodeVisionAVR можно воспользоваться: • внешним отладчиком AVR Studio для отладки на программном уровне. Компилятор Си CodeVisionAVR версии 1.24.lx Standart позволяет работать с отладчиком Atmel AVR Studio версий 3 и 4.06 (или более поздней); • встроенным терминалом для отладки на программно-аппаратном уровне встраиваемых систем. Кроме того, в настоящее время существует множество программных продуктов, позволяющих наглядно и качественно отлаживать программы для микроконтрол- леров фирмы Atmel. Например, можно порекомендовать программные продукты Visual Micro Lab от компании Advanced Micro Tools (http://www.amctools.com/) (Рис. 3.34) и Proteus VSM от Labcenter Electronics (http://www.labcenter.co.uk/) (Рис. 3.35).
110 Глава 3. Работа в IDE Code Vision A VR Рис. 3.34. Окно программы Visual Micro Lab. «о. х Л’~ М С + О № W» 44 * t Я * о ф u I (ED I 0 СН1 ОЙ ос оно лс СН2 LJ ос ано лс Рис. 3.35. Окно программы Proteus VSM.
3.2. Работа с проектами 111 Эти программные продукты позволяют достаточно качественно эмулировать реальную схему, подключать к любой точке схемы такие виртуальные инструмен- ты, как генераторы, осциллографы и т. д. Это позволяет обойтись без единой пай- ки, делает отладку программы более наглядной, даёт возможность последователь- но передвигаться по коду и смотреть, как изменяются входные и выходные сигна- лы при выполнении той или иной команды. К сожалению, объём данной книги не позволяет подробно описать эти про- граммные продукты. Чтобы запускать эти и другие программы, не выходя из IDE CodeVisionAVR, просто добавьте их в меню Tools (Инструменты) (см. Команда Tools -> Configure (Инструменты Конфигурировать)). Работа с отладчиком AVR Studio Для использования отладчика AVR Studio необходимо установить на компью- тер соответствующую версию AVR Studio, а затем в IDE CodeVisionAVR опреде- лить (Рис. 3.36, 3.37) местоположение и имя файла отладчика (см. Команда Settings Debugger (Настройки Отладчик)). Рис. 3.36. Вид диалогового окна Debugger Settings (Настройки отладчика) для использования отладчика AVR Studio версии 3. Рис. 3.37. Вид диалогового окна Debugger Settings (Настройки отладчика) для использова- ния отладчика AVR Studio версии 4. В настройках компилятора на закладке С Compiler (Компилятор Си) (см. За- кладка С Compiler (Компилятор Си)) в опции File Output Format(s) (Формат(ы) вы- ходных файлов) следует выбрать набор файлов, в котором присутствует расшире- ние COF (Рис. 3.38). Тогда компилятор будет генерировать файл с расширением .cof, который требуется AVR Studio для отладки программы на исходном уровне Си. После всех этих настроек следует построить проект (см. Построение проекта). Рис. 3.38. Выбор генерируемых файлов с расширением СОЕ
И2 Глава 3. Работав IDECodeVisionAVR Следует убедиться, что в рабочей папке проекта появился файл с расширени- ем .cof (Рис. 3.39).Теперь можно запустить отладчик (см. Команда Tools -> Debugger (Инструменты -> Отладчик)). Если выбран отладчик AVR Studio версии 3, то запустится AVR Studio соответс- твующей версии, куда автоматически будет загружен файл с расширением .cof (Рис. 3.40). При этом жёлтая стрелка будет указывать на оператор, который будет выполняться следующим. Следует отметить, что на некоторых компьютерах при выполнении команды Tool -> Debugger (Инструменты Отладка) AVR Studio версии 3 запускается, но файл не загружается. Тогда следует самостоятельно запустить AVR Studio версии 3, выбрать команду File -> Open,,, (Файл Открыть...) и выбрать соответствующий файл с расширением .cof. AVR Studio версии 3 позволяет наблюдать, что происходит в микроконтролле- ре при пошаговом выполнении программы, т. е. при последовательном выполне- нии каждого оператора. При этом можно следить, как изменяются те или иные переменные в ходе выполнения программы, наблюдать, что происходит в регист- рах, портах, FLASH-, SRAM- и EEPROM-памяти и т. д. Для этого следует выбрать в меню View (Вид) соответствующие опции или воспользоваться соответствую- щими кнопками на панели инструментов. Тогда окно AVR Studio версии 3 приоб- ретёт вид, показанный на Рис. 3.41. Количество дополнительных окон, их вид и расположение могут отличаться. Рис. 3.39. Файл с расширением .cof в рабочей папке проекта.
3.2. Работа с проектами ИЗ Рис. 3.40. AVR Studio версии 3 с загруженным файлом. Все изменения, происходящие в микроконтроллере после выполнения теку- щего оператора, выделяются красным цветом. Рис. 3.41. Вид окна AVR Studio версии 3 для наблюдения изменений в микроконтроллере при выполнении программы.
114 Глава 3. Работа в IDE Code VisionA VR В AVR Studio версии 3 (в отличие от AVR Studio версии 4) для связи с UART си- мулируемого чипа AVR можно использовать Terminal I/O (Терминал ввода/выво- да). Для этого при конфигурировании проекта на закладке С Compiler (Компиля- тор Си) (см. Закладка С Compiler (Компилятор Си)) следует выбрать набор файлов с форматом COF и опцию Use the Terminal I/O in AVR Studio (Использовать терми- нал ввода/вывода в AVR Studio). Чтобы получить больше информации об использовании AVR Studio версии 3 в качестве отладчика, следует обратиться к её системе помощи (команда Help -» Help Topics). Если выбран отладчик AVR Studio версии 4, то после вызова отладчика (см. Ко- манда Tools -> Debugger (Инструменты -> Отладчик)) запустится AVR Studio соот- ветствующей версии и появится окно Welcome to AVR Studio 4 (Добро пожаловать в AVR Studio 4) (Рис. 3.42). Рис. 3.42. Окно Welcome to AVR Studio 4 (Добро пожаловать в AVR Studio 4). В этом окне следует щёлкнуть по кнопке Open (Открыть). В появившемся окне Open SaveFile or ObjectFile (Открыть сохранённый или объектный файл) выбрать со- ответствующий файл с расширением .cof и щёлкнуть по кнопке Открыть (Рис. 3.43). Рис. 3.43. Окно Open SaveFile or ObjectFile (Открыть сохранённый или объектный файл).
3.2. Работа с проектами 115 После этого будет предложено выбрать Debug Platform (Платформа отладки) и Device (Устройство). Если отсутствуют аппаратные средства для отладки про- грамм, то в качестве платформы отладки следует выбрать AVR Simulator (Симуля- тор AVR). В качестве устройства следует выбрать микроконтроллер, для которого разрабатывалась программа, и щёлкнуть по кнопке Finish (Финиш) (Рис. 3.44). Рис. 3.44. Выбор Debug Platform (Платформа отладки) и Device (Устройство). После этого файл будет автоматически загружен в AVR Studio версии 4 и будет создан новый проект AVR Studio, связанный с файлом СОЕ Справа в окне AVR Studio версии 4 располагается текст исходной программы на Си. Жёлтая стрелка указывает на оператор, который будет выполняться следу- ющим. Слева располагается дерево всей периферии данного микроконтроллера. AVR Studio версии 4 позволяет наблюдать, что происходит в микроконтролле- ре при пошаговом выполнении программы, т. е. при последовательном выполне- нии каждого оператора. При этом можно следить, как изменяются те или иные переменные в ходе выполнения программы, наблюдать, что происходит в регист- рах, портах, FLASH-, SRAM- и EEPROM-памяти и т. д. Это можно наблюдать в окне слева, а можно выбрать в меню View (Вид) соответствующие опции или вос- пользоваться соответствующими кнопками на панели инструментов. Тогда окно AVR Studio версии 4 может приобрести вид, показанный на Рис. 3.45. Количество дополнительных окошек, их вид и расположение могут от- личаться. Пример использования AVR Studio версии 4 приведён в главе Пример исполь- зования CodeWizardAVR. Чтобы получить больше информации об использовании AVR Studio версии 4 в качестве отладчика, следует обратиться к её системе помощи (команда Help -> AVR Studio User Guide). В AVR Studio версии 4.06 (или более поздней) объектный файл COFпозволя- ет наблюдать structures (структуры) и unions (объединения). Таким обра- зом, настоятельно рекомендуется использовать AVR Studio версии 4.06
116 Глава 3. Работа в IDE Code VisionA VR (или более поздней) вместо версии 3, которая не поддерживает эту харак- теристику. AVR Studio 4, предшествующая версии 4.06, не поддерживает расширенный объектный файловый формат COF, так что она не может использоваться с CodeVisionAVR. Рис. 3.45. Вид AVR Studio версии 4 с дополнительными окнами. Терминал последовательной связи Встроенный терминал последовательной связи применяется для отладки сис- тем, которые используют последовательную связь (протоколы RS232, RS422, RS485). До использования терминала следует задать его настройки (см. Команда Settings Terminal (Настройки -> Терминал)). После запуска терминала (см. Команда Tools -> Terminal (Инструменты Тер- минал)) в области редактирования будет открыто окно Terminal (Терминал) (Рис. 3.46).
3.2. Работа с проектами 117 Рис. 3.46. Окно Terminal (Терминал) при установленной связи. В верхней части окна расположены: • кнопка Disconnect/Connect (Разъединить/Соединить), которая позволяет прервать/возобновить последовательную связь. Когда связь установлена, окно Terminal (Терминал) имеет вид, показанный на Рис. 3.46. Если теперь щёлкнуть по кнопке Disconnect/Connect, то связь будет прервана и окно Terminal (Терминал) приобретёт вид, показанный на Рис. 3.47; Рис. 3.47. Окно Terminal (Терминал) при прерванной связи. • кнопка Send (Послать). При нажатии этой кнопки терминал передаст сим- вол, шестнадцатеричное значение ASCII кода которого введено в окошке редактирования Hex Code (Шестнадцатеричный код); • кнопка Rx File (Принять в файл) позволяет задать файл, в который можно сохранять получаемые символы. При нажатии этой кнопки появится диа- логовое окно Save received characters to a file (Сохранять полученные симво- лы в файл), в котором следует задать имя, выбрать расширение для этого файла и щёлкнуть по кнопке Сохранить (Рис. 3.48); (Binary files (".bin) | Any files ( ) Puc. 3.48. Диалоговое окно Save received characters to a file (Сохранять полученные символы в файл).
118 Глава 3. Работа в IDE Code Vision A VR • кнопка Тх File (Передать из файла) позволяет задать файл, содержимое ко- торого следует передать через последовательный порт. При нажатии этой кнопки появится диалоговое окно Transmit file (Передаваемый файл), в ко- тором следует выбрать соответствующий файл и щёлкнуть по кнопке От- крыть (Рис. 3.49); Рис. 3.49. Диалоговое окно Transmit file (Передаваемый файл). • кнопка Hex/ASCII (Шестнадцатеричный/ASCII) позволяет изменить ре- жим отображения символов соответственно в шестнадцатеричном (Hex) виде или в виде ASCII (Рис. 3.50); а) б) Рис. 3.50. Отображение символов в окне Terminal (Терминал) в ASCII (а) и шестнадцатиричном (Hex) (б) виде. • кнопка Clear (Очистить) позволяет очистить окно терминала; • кнопка Reset Chip (Сбросить чип) позволяет сбросить чип на подключён- ной плате STK200+/300, VTEC-ISP, DT006, ATCPU или на плате разработ- чика MegaZOOO.
3.2. Работа с проектами 119 Внизу окна Terminal (Терминал) есть строка состояния, в которой отображаются: • подключённый порт компьютера; • параметры связи (скорость передачи, количество битов данных, контроль чётности и количество стоповых битов); • режим установления связи при коммуникации; • режим отображения полученных символов; • тип эмулируемого терминала; • состояние (включён или выключен) эхоконтроля переданных символов. 3.2.5. Запись программы в чип AVR Для записи скомпилированной программы непосредственно в чип AVR, IDE CodeVisionAVR имеет встроенный In-System AVR Chip Programmer (Внутрисхем- ный программатор чипов AVR). Встроенный программатор имеет два буфера памяти: • буфер FLASH-памяти; • буфер EEPROM-памяти. Программное обеспечение внутрисхемного программатора предназначено работать вместе со следующими аппаратными программаторами: • Atmel STK500/AVRISP; • AVRProg (AVR910); • Kanda Systems STK200+/300; • Dontronics DT006; • Vogel Elektronik VTEC-ISP; • Futurlec JRAVR; • MicroTronics ATCPU/Mega2000. При отсутствии перечисленных выше фирменных программаторов можно по- рекомендовать самостоятельно собрать программатор по схеме, приведённой на Рис. 3.51. Рис. 3.51. Электрическая принципиальная схема программатора.
120 Глава 3. Работа в IDE Code VisionA VR Аналогичным программатором комплектуются отладочные платы STK200/STK300. Разъём XI следует подключить к свободному LPT-порту ком- пьютера, а указанные справа цепи — к соответствующим выводам микроконтрол- лера. Пример подключения микроконтроллера к программатору к AT90S2313 по- казан на Рис. 3.52. Рис. 3.52. Подключение к программатору микроконтроллера AT90S2313. Внутрисхемный программатор позволяет программировать микроконтролле- ры, не извлекая их из схемы разрабатываемого устройства. Подробнее о внутри- схемном программировании см. прикладное описание «AVR910: In-System Programming», которое можно найти на сайте разработчиков микроконтроллеров AVR http://www.atmel.com. Перед использованием встроенного программатора следует задать его на- стройки (см. Команда Settings Programmer (Настройки Программатор)). Если используется самодельный программатор, схема которого приведена на Рис. 3.51, то в настройках следует выбрать тип Kanda Systems STK200+/300, как показано на Рис. 3.53. Programmer Settings AVR Chip Programmer Type: | Kanda Systems S T K200+Z300 Kanda Systems STK2004-/300 Atmel ST K500/AVRISP Atmel AVR Prog (AVR 910) Dontronics DT006 Vogel Elektronik VTEC-ISP Futurlec JRAVR MicroT ronics AT CPU/Mega2000 Puc. 3.53. Выбор типа для самодельного программатора.
3.2. Работа с проектами 121 После запуска программатора (см. Команда Tools -> Chip Programmer (Инстру- менты программатор чипов)) будет открыто окно CodeVisionAVR Chip Programmer (Чип-программатор CodeVisionAVR) для выбранного программатора (Рис. 3.54). Рис. 3.54. Диалоговое окно CodeVisionAVR Chip Programmer (Чип-программатор CodeVisionAVR). В этом диалоговом окне можно задать следующие опции программирования. • Chip (Чип) — в этом окошке в выпадающем списке можно выбрать микро- контроллер AVR, который требуется запрограммировать. • Кнопка Programm All — позволяет запрограммировать весь чип. При этом автоматически будут выполнены следующие операции: — в чипе стирается FLASH- и EEPROM-память; - FLASH- и EEPROM -память проверяются на чистоту; - FLASH -память программируется и проверяется; - EEPROM -память программируется и проверяется; — Программируются Fuse- и Lock- биты микроконтроллера; • Кнопка Reset Chip — позволяет сбросить чип на плате разработчика; • В поле Flash выводится начальный (Start) и конечный (End) адреса занятой области Flash-памяти, а также контрольная сумма (Checksum); • В поле EEPROM выводится начальный (Start) и конечный (End) адреса за- нятой области EEPROM-памяти, а также контрольная сумма (Checksum); В поле Chip Programming Options (Опции программирования чипа) доступны следующие опции:
122 Глава 3. Работа в IDE Code VisionA VR • FLASH Lock Bits (Биты блокировки FLASH) — в этом поле (Рис. 3.55) мож- но выбрать одну из трех степеней защиты программы, содержащейся в микроконтроллере, от несанкционированного доступа: — No Protection (Без защиты) — этот вариант выбирается, если защита не требуется; — Programming disabled (Запретить программирование) — при выборе этого варианта будет запрещено перепрограммирование чипа; — Programming and Verification disabled (Запретить программирование и проверку) — при выборе этого варианта будет запрещено перепрограм- мирование и чтение чипа. Рис. 3.55. Поле FLASH Lock Bits (Биты блокировки FLASH). Подробнее о защите информации, содержащейся в чипе, читайте описание на используемый микроконтроллер (см. Команда Help -> AVR Data Sheets (Помощь Описания AVR)). • Check Signature (Проверка сигнатуры) — если выбрать эту опцию, то перед любой операцией будет проверяться сигнатура чипа. См. также Команда Read -> Chip Signature (Чтение -» Сигнатура чипа). • Check Erasure (Проверка стирания) — если выбрать эту опцию, то после стирания чипа будет проведена его проверка на чистоту. Чтобы ускорить процесс программирования, эту опцию выбирать не следует.
3.2. Работа с проектами 123 • Preserve EEPROM (Сохранить EEPROM) — если выбрать эту опцию, то при стирании чипа содержимое EEPROM будет сохранено. • Verify (Проверка) — если выбрать эту опцию, то после программирования будет произведена проверка записанной информации. Чтобы ускорить процесс программирования, эту опцию лучше отклонить. Если выбранный микроконтроллер имеет Fuse Bit(s) (Биты-предохранители), которые можно запрограммировать, то появится поле Fuse Bit(s) (Биты-предох- ранители) (Рис. 3.56). Рис. 3.56. Поле Fuse Bit(s) (Биты-предохранители). Используя это поле, можно установить различные опции чипа, которые ука- заны в описании используемого микроконтроллера (см. Команда Help -+AVR Data Sheets (Помощь —> Описания AVR)). Если Fuse Bit (Предохранительный бит) ВЫБРАН, то соответствующий предохранительный бит БУДЕТ УСТАНОВЛЕН В 0, предохранитель счи- тается запрограммированным (в соответствии с соглашением в описани- ях Atmel). Если Fuse Bit (Предохранительный бит) НЕ ВЫБРАН, то соответствую- щий предохранительный бит БУДЕТ УСТАНОВЛЕН В 1, предохранитель считается незапрограммированным.
124 Глава 3. Работа в IDE Code Vision A VR Если выбранный микроконтроллер имеет Boot Lock Bit(s) (Биты блокировки загрузчика), которые можно запрограммировать, то появятся поля Boot Lock Bit(s) (Биты блокировки загрузчика) (Рис. 3.57). Рис. 3.57. Поля Boot Lock Bit(s) (Биты блокировки загрузчика). Используя эти поля, можно установить различные опции чипа, которые ука- заны в описании используемого микроконтроллера (см. Команда Help -» AVR Data Sheets (Помощь —> Описания AVR)). Как и основное окно программы, диалоговое окно CodeVisionAVR Chip Programmer (Чип-программатор CodeVisionAVR) имеет строку меню. В этой стро- ке выведены названия меню, которые предоставляют доступ к их командам (Рис. 3.58). File Edit Program Read Compare Help Puc. 3.58. Строка меню диалогового окна CodeVisionAVR Chip Programmer (Чип-программатор CodeVisionAVR). Меню File (Файл) В состав меню File (Файл) входит несколько команд. В Табл. 3.6 даётся их краткое описание.
3.2. Работа с проектами 125 Таблица 3.6. Команды меню File (Файл) Команда Назначение Load FLASH (Загрузить FLASH) Позволяет загрузить содержимое файла в буфер FLASH -памяти Load EEPROM (Загрузить EEPROM) Позволяет загрузить содержимое файла в буфер EEPROM-памяти Save FLASH (Сохранить FLASH ) Позволяет сохранить в файл содержимое буфера FLASH-памяти Save EEPROM (Сохранить EEPROM) Позволяет сохранить в файл содержимое буфера EEPROM-памяти Close (Закрыть) Закрывает диалоговое окно CodeVisionAVR Chip Programmer (Чип-программатор CodeVisionAVR) Команда File Load FLASH (Файл Загрузить FLASH) Эта команда позволяет загрузить содержимое файла в буфер FLASH-памяти. После выбора этой команды (Рис. 3.59) появится диалоговое окно Load File to FLASH Buffer (Загрузить файл во FLASH-буфер), показанное на Рис. 3.60. Рис. 3.59. Выбор команды File -> Load FLASH (Файл -> Загрузить FLASH). lintel HEX files (х.hex) | Binary files (".bin) Puc. 3.60. Диалоговое окно Load File to FLASH Buffer (Загрузить файл во FLASH-буфер).
126 Глава 3. Работа в IDE Code VisionA VR В этом окне следует выбрать файл в соответствующем формате и щёлкнуть по кнопке Открыть. Поддерживаются следующие файловые форматы: • Atmel ROM (*.rom); • Intel HEX (Шестнадцатеричный) (*.hex); • Binary (Двоичный) (*.bin). После загрузки файла во FLASH-буфер в поле FLASH соответственно изме- нятся начальный (Start) и конечный (End) адреса той области FLASH-памяти, ко- торая будет изменена при программировании, т. е. будет занята программой, а также Checksum (Контрольная сумма) (Рис. 3.61). Начальный и конечный адреса при необходимости можно изменить. а) CodeVisionAVR Chip Programmer - Dontronics DTOO6 б) Рис. 3.61. Начальный (Start) и конечный (End) адреса области FLASH-памяти, которая будет занята программой, а также Checksum (Контрольная сумма) до загрузки файла во FLASH-буфер (а) и после (б). Команда File -> Load EEPROM (Файл -> Загрузить EEPROM) Эта команда позволяет загрузить содержимое файла в буфер EEPROM-na- мяти. После выбора этой команды (Рис. 3.62) появится диалоговое окно Load File to EEPROM Buffer (Загрузить файл в EEPROM-буфер), показанное на Рис. 3.63.
3.2. Работа с проектами 127 Рис. 3.62. Выбор команды File -> LoadEEPROM (Файл -> Загрузить EEPROM). Intel HEX files (M.hex) Binary files (" bin] Puc. 3.63. Диалоговое окно Load File to EEPROM Buffer (Загрузить файл в EEPROM-буфер). В этом окне следует выбрать файл в соответствующем формате и щёлкнуть по кнопке Открыть. Поддерживаются следующие файловые форматы: • Atmel ЕЕР (*.еер); • Intel HEX (Шестнадцатеричный) (*.hex); • Binary (Двоичный) (*.bin). После загрузки файла в EEPROM-буфер в поле EEPROM соответственно изме- нятся начальный (Start) и конечный (End) адреса области EEPROM-памяти, кото- рая будет изменена при программировании, а также Checksum (Контрольная сумма) (Рис. 3.64). Начальный и конечный адреса при необходимости можно изменить. Edit Program Read Compare Help deVisionAVR Chip Programmer - Kan da Systems STK2O... |X j a)
128 Глава3. Работав IDECodeVisionAVR б) Рис. 3.64. Начальный (Start) и конечный (End) адреса области EEPROM-памяти, которая будет занята программой, а также Checksum (Контрольная сумма) до загрузки файла в EEPROM-буфер (а) и после (б). Команда File -> Save FLASH (Файл -> Сохранить FLASH ) Эта команда позволяет сохранить содержимое буфера FLASH-памяти в файл. После выбора этой команды (Рис. 3.65) появится диалоговое окно Save FLASH Buffer to File (Сохранить FLASH-буфер в файл), показанное на Рис. 3.66. Рис. 3.65. Выбор команды File -> Save FLASH (Файл -> Сохранить FLASH). Binary file (*bir Puc. 3.66. Диалоговое окно Save FLASH Buffer to File (Сохранить FLASH-буфер в файл).
3.2. Работа с проектами 129 В этом окне следует выбрать соответствующий формат файла, ввести его имя и щёлкнуть по кнопке Сохранить. Поддерживаются следующие файловые форматы: • Atmel ROM (*.rom); • Intel HEX (Шестнадцатеричный) (*.hex); • Binary (Двоичный) (*.bin). Команда File Save EEPROM (Файл -> Сохранить EEPROM) Эта команда позволяет сохранить содержимое буфера EEPROM-памяти в файл. После выбора этой команды (Рис. 3.67) появится диалоговое окно Save EEPROM Buffer to File (Сохранить EEPROM-буфер в файл), показанное на Рис. 3.68. Рис. 3.67. Выбор команды File Save EEPROM(Файл Сохранить EEPROM) Intel HEX file (’.hex) [Binary file(xbin) Puc. 3.68. Диалоговое окно Save EEPROM Buffer to File (Сохранить EEPROM-буфер в файл). В этом окне следует выбрать соответствующий формат файла, ввести его имя и щёлкнуть по кнопке Сохранить. Поддерживаются следующие файловые форматы: • Atmel EEP (*.еер); • Intel HEX (Шестнадцатеричный) (*.hex); • Binary (Двоичный) (*.bin).
130 Глава 3. Работа в IDECodeVisionAVR Команда File -> Close (Файл Закрыть) Эта команда позволяет закрыть диалоговое окно CodeVisionAVR Chip Programmer (Чип-программатор CodeVisionAVR). После выбора этой команды (Рис. 3.69) диалоговое окно закрывается и осу- ществляется возврат в основное окно программы. fe Load FLASH Load EEPROM Й Save FLASH e Save EEPROM CodeVisionAVR Chip Programmer - Dontronics DT006 File | Puc. 3.69. Выбор команды File -> Close (Файл -> Закрыть). Меню Edit (Правка) В состав меню Edit (Правка) входит две команды. В Табл. 3.7 даётся их краткое описание. Таблица 3.7. Команды меню Edit (Правка) Команда Назначение FLASH Позволяет отредактировать содержимое буфера FLASH-памяти EEPROM Позволяет отредактировать содержимое буфера EEPROM-памяти Команда Edit -> FLASH (Правка -> FLASH) Эта команда позволяет просмотреть и отредактировать содержимое буфера FLASH-памяти. После выбора этой команды откроется окно Edit FLASH Buffer (Редактирова- ние FLASH-буфера), показанное на Рис. 3.70. П Edit FLASH Buffet xQ_ _ х1 x2 хЗ x4 x5 хб x7 x8 ООО0ИЕ JFFE CFFD CFFC CFFB CFFA C046 CFF8 CFF7 CFF6 CFF5 CFF4 CFF3 OOFE 0001 00E0 OOlxOOTXOOOO 94F8 27EE BBEC BFE5 E1F8 BDF1 BDE1 E08D E0A2 27BB 83ED 958A F7E9 E080 002x E092 E6A0 93ED 9701 F7E9 E1EC EOFO 95C8 9631 2D80 95C8 9631 2D90 9700 F0B9 95C8 003x 9631 2DA0 95C8 9631 2DB0 95C8 9631 2C10 95C8 9631 2F6E 2F7F 2DF0 2DE1 95C8 9631 004x 920D 9701 F7D9 2FE6 2FF7 CFE1 E5EF BFED E0E2 BFEE EECO EODO C01C 93AA 93EA 93FA 005x B7EF 93EA D027 91E0 00E0 OFEE 93E0 00E0 60E1 93E0 00E0 91A0 00E0 3FAF F419 EFEE ООбх 93E0 00E0 D01C 91E9 BFEF 91F9 91E9 91A9 9518 EFEF BBE4 D013 27EE BDEF E0E5 BDEE 007x D009 27EE BFE8 E8E0 BFE9 27EE BFEB 9478 CFFF CFFF EFE8 EFF8 BDFD BDEC 9508 91E0 008x 00E0 BBE5 9508 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF Puc. 3.70. Окно Edit FLASH Buffer (Редактирование FLASH-буфера).
3.2. Работа с проектами 131 В этом окне будет отображено содержимое FLASH-буфера. Выделенное зна- чение можно отредактировать, просто введя новое значение. Перемещаться в ок- не (выделять определённое значение) можно с помощью мыши или с помощью клавиш управления курсором. Если требуется заполнить какой-либо блок FLASH-памяти одинаковыми зна- чениями, то в окне Edit FLASH Buffer (Редактирование FLASH-буфера) следует щёлкнуть ПРАВОЙ клавишей мыши, а затем в появившемся окошке щёлкнуть ЛЕВОЙ клавишей мыши по строке Fill Memory Block (Заполнение блока памяти) (Рис. 3.71). й Edit FLASH Buffer OOOx 001 x 002x 003x 004x 005x 006x 007x 008x xO х1 x2 хЗ x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF OOFE 958A 9700 2DE1 93AA 3FAF BDEF E0E5 001A E092 9631 920D B7EF 93E0 D009 00E0 CFFD 94F8 93ED CFFE 0000 E6A0 2DA0 95C8 9701 93EA 00E0 27EE BBE5 9508 FFFF CFFC 27EE 9701 9631 2FE6 91E0 CFF8 BDF1 95C8 CFF7 BDE1 9631 CFF6 E08D 2D 80 F7D9 D027 D01C 91E9 BFE8 E8E0 CFFB CFFA C046 BBEC BFE5 E1F8 F7E9 E1EC EOFO 2DB0 95^8.„..9.63.1..2C.1Q..95С8-Д631 2FF7 ..................... 00E0 BFEF BFE9 FFFF CF|| OFEF 91F9 27EE FFFF Fill Memory Block I If**' 91E9 BFEB FFFF 91A9 9478 FFFF fEE TO0^y93EO 9518 EFEF CFFF CFFF FFFF FFFF CFF4 27BB 9631 2F7F CFF3 93ED 2D90 2DF0 CFF5 E0A2 95C8 2F6E EECO EODO C01C 00E0 91A0 BBE4 D013 EFE8 EFF8 FFFF FFFF 0001 F7E9 F0B9 95C8 93EA F419 00E0 27EE BDFD BDEC 9508 FFFF FFFF FFFF 00E0 E080 95C8 9631 93FA EFEE BDEE 91E0 FFFF Puc. 3.71. Вызов диалогового окна Fill Memory Block (Заполнение блока памяти). После этого появится диалоговое окно Fill Memory Block (Заполнение блока памяти), в котором следует ввести Start Address (Начальный адрес) и End Address (Конечный адрес) соответствующего блока памяти, Fill Value (Значение для за- полнения) и щёлкнуть по кнопке ОК (Рис. 3.72). [3 Fill Memory Block |X i Start Address: |0014 h End Address: |0069 h Fill Value: |mAA h ✓ QK | X Cancel | Puc. 3.72. Диалоговое окно Fill Memory Block (Заполнение блока памяти). После этого соответствующий блок буфера FLASH-памяти будет заполнен одинаковыми значениями (Рис. 3.73).
132 Глава 3. Работа в IDE Code Vision A VR Рис. 3.73. Блок буфера FLASH-памяти, заполненный одинаковыми значениями. После закрытия окна Edit FLASH Buffer (Редактирование FLASH-буфе- ра) все внесённые изменения АВТОМАТИЧЕСКИ сохраняются. Команда Edit -> EEPROM (Правка -> EEPROM) Эта команда позволяет отредактировать содержимое буфера EEPROM-памя- ти. После выбора этой команды откроется окно Edit EEPROM Buffer (Редактиро- вание EEPROM-буфера), показанное на Рис. 3.74. Рис. 3.74. Окно Edit EEPROM Buffer (Редактирование EEPROM-буфера). В этом окне будет отображено содержимое EEPROM-буфера. Выделенное значение можно отредактировать, просто введя новое значение. Если требуется заполнить какой-либо блок EEPROM-памяти одинаковыми значениями, то в окне Edit EEPROM Buffer (Редактирование EEPROM-буфера) следует щёлкнуть ПРАВОЙ клавишей мыши, а затем в появившемся окошке щёлкнуть ЛЕВОЙ клавишей мыши по строке Fill Memory Block (Заполнение бло- ка памяти) (Рис. 3.75). Рис. 3.75. Вызов диалогового окна Fill Memory Block (Заполнение блока памяти).
3.2. Работа с проектами 133 После этого появится диалоговое окно Fill Memory Block (Заполнение блока памяти), в котором следует ввести Start Address (Начальный адрес), End Address (Конечный адрес) соответствующего блока памяти, а также Fill Value (Значение для заполнения) и щёлкнуть по кнопке ОК (Рис. 3.76). Рис. 3.76. Диалоговое окно Fill Memory Block (Заполнение блока памяти). После этого соответствующий блок буфера EEPROM-памяти будет заполнен одинаковыми значениями (Рис. 3.77). Рис. 3.77. Блок буфера EEPROM-памяти, заполненный одинаковыми значениями. После закрытия окна Edit ЕЕРВОМ Buffer (Редактирование EEPROM-бу- фера) все внесённые изменения автоматически сохраняются. Меню Program (Программирование) В состав меню Program (Программирование) входит несколько команд. В Табл. 3.8 даётся их краткое описание. Таблица 3.8. Команды меню Program (Программирование) Комавда Назначение Erase Chip (Стереть чип ) Позволяет стереть чип Blank Chek (Проверить на чистоту) Позволяет проверить, очищен ли чип FLASH Позволяет запрограммировать ТОЛЬКО FLASH-память EEPROM Позволяет запрограммировать ТОЛЬКО EEPROM-память Lock Bits (Биты блокировки) Позволяет запрограммировать ТОЛЬКО £осА>биты Fuse Bit(s) (Биты-предохранители) Позволяет запрограммировать ТОЛЬКО /игс-биты All (Все) Позволяет запрограммировать ВСЁ сразу
134 Глава 3. Работа в IDE Code VisionA VR Команда Program Erase Chip (Программирование -> Стереть чип) Эта команда позволяет стереть содержимое FLASH- и EEPROM-памяти чипа. После выбора этой команды содержимое FLASH- и EEPROM-памяти будет очищено, т. е. во все ячейки будут записаны единицы. Если выбрана опция Check Erasure (Проверка стирания) (Рис. 3.78), то после выполнения команды Program -> Erase Chip (Программирование -> Стереть чип) FLASH-память чипа будет АВТОМАТИЧЕСКИ проверена на чистоту (см. Ко- манда Program -> Blank Chek (Программирование -> Проверить на чистоту)). Рис. 3.78. Опция Check Erasure (Проверка стирания) выбрана. Если выбрана опция Preserve EEPROM (Сохранить EEPROM) (Рис. 3.79), то содержимое EEPROM-памяти перед стиранием чипа будет сохранено, а после стирания — восстановлено. Рис. 3.79. Опция Preserve EEPROM (Сохранить EEPROM) выбрана. При этом сначала откроется окно Saving EEPROM contents (Сохранение со- держимого EEPROM) с линейной диаграммой, отображающей процесс сохране- ния (Рис. 3.80). Рис. 3.80. Окно Saving EEPROM contents (Сохранение содержимого EEPROM) с линейной диаграммой. После выполнения команды Program -» Erase Chip (Программирование -> Стереть чип) содержимое EEPROM-памяти чипа будет АВТОМАТИЧЕСКИ вос- становлено. При этом откроется окно Restoring EEPROM contents (Восстановле-
3.2. Работа с проектами 135 ние содержимого EEPROM) с линейной диаграммой, отображающей процесс восстановления (Рис. 3.81). Рис. 3.81. Окно Saving EEPROM contents (Сохранение содержимого EEPROM) с линейной диаграммой. Таким образом, при выбранной опции Preserve EEPROM (Сохранить EEPROM) после выполнения команды Program Erase Chip (Программирование стереть чип) содержимое EEPROM останется прежним. Команда Program Blank Chek (Программирование -> Проверить на чистоту) Эта команда позволяет проверить на чистоту содержимое FLASH- и EEPROM -памяти чипа. После выбора этой команды откроется окно FLASH Blank Cheking (Проверка FLASH-памяти на чистоту) с линейной диаграммой, отображающей процесс проверки (Рис. 3.82). Рис. 3.82. Окно FLASH Blank Cheking (Проверка FLASH-памяти на чистоту) с линейной диаграммой. Если при проверке обнаружится неочищенная ячейка, то будет выведено со- ответствующее сообщение (Рис. 3.83) с указанием адреса (address) и содержимого (data) соответствующей ячейки. Рис. 3.83. Сообщение о неочищенной ячейке. Команда Program -> FLASH (Программирование FLASH) Эта команда позволяет запрограммировать во FLASH-память чипа содержи- мое FLASH-буфера.
136 Глава 3. Работа в IDE Code VisionA VR После выбора этой команды (Рис. 3.84) откроется окно FLASH Programming (Программирование FLASH) с линейной диаграммой, отображающей процесс программирования (Рис. 3.85). File Edit FLASH С Programming disabled Program £111 Q? Re jet EEPROM------------- Read Compare Help Checksum: FEOOh IX Erase Chip Chip: fl X Blank Check Start: 10 h End: Start: pX EEPROM 4" Check X Lock Bits X Fuse Bit(s) ChipR-------------------- FUS X AH (♦ Ng Protection CodeVisionAVR Chip Programmer - Kanda Systems STK2O, Program FLASH Puc. 3.84. Выбор команды Program -> FLASH(Программирование FLASH). Puc. 3.85. Окно FLASH Programming (Программирование FLASH) с линейной диаграммой. По завершении программирования это окно автоматически закроется. Если выбрана опция Verify (Проверка) (Рис. 3.86), то после выполнения ко- манды Program —> FLASH (Программирование -» FLASH) содержимое Flash-па- мяти будет автоматически проверено. К? Check Signature Р Check Erasure Р* Preserve ЕЕ PRO м(Р Verify Рис. 3.86. Опция Verify (Проверка) выбрана. При этом откроется окно Verifying the FLASH Programming (Проверка про- граммирования FLASH) с линейной диаграммой, отображающей процесс про- верки (Рис. 3.87).
3.2. Работа с проектами 137 Рис. 3.87. Окно Verifying the FLASH Programming (Проверка программирования FLASH) с линейной диаграммой. Команда Program EEPROM (Программирование -> EEPROM) Эта команда позволяет запрограммировать в EEPROM-память чипа содержи- мое EEPROM-буфера. После выбора этой команды откроется окно EEPROM Programming (Про- граммирование EEPROM) с линейной диаграммой, отображающей процесс про- граммирования (Рис. 3.88). Рис. 3.88. Окно EEPROM Programming (Программирование EEPROM) с линейной диаграммой. По завершении программирования это окно автоматически закроется. Если выбрана опция Verify (Проверка) (Рис. 3.89), то после выполнения ко- манды Program EEPROM (Программирование -> EEPROM) содержимое EEPROM -памяти будет автоматически проверено. Рис. 3.89. Опция Verify (Проверка) выбрана. При этом откроется окно Verifying the EEPROM Programming (Проверка про- граммирования EEPROM) с линейной диаграммой, отображающей процесс про- верки (Рис. 3.90). Рис. 3.90. Окно Verifying the EEPROM Programming (Проверка программирования EEPROM) с линейной диаграммой.
138 Глава 3. Работав IDECodeVisionAVR Команда Program -> Lock Bits (Программирование -> Биты блокировки) Эта команда позволяет запрограммировать Lock-биты (Биты блокировки) чипа. После выбора этой команды (Рис. 3.91) Lock-биты (Биты блокировки) чипа будут запрограммированы в соответствии с опциями, заданными в поле FLASH Lock Bits (Биты блокировки FLASH) (Рис. 3.55). Рис. 3.91. Выбор команды Program Lock Bits (Программирование -> Биты блокировки). Команда Program -э Fuse Bit(s) (Программирование -> Биты-предохранители) Эта команда позволяет запрограммировать Fuse-биты (Биты-предохраните- ли) чипа. После выбора этой команды Fuse-биты (Биты-предохранители) чипа будут запрограммированы в соответствии с опциями, заданными в поле Fuse Bit(s) (Би- ты-предохранители) (Рис. 3.56). Команда Program АН (Программирование Всё) Эта команда позволяет запрограммировать весь чип сразу. После выбора этой команды автоматически будут выполнены следующие операции. • В чипе стирается FLASH- и EEPROM-память (см. Команда Program -> Erase Chip (Программирование -> Стереть Чип)); • FLASH- и EEPROM-память проверяются на чистоту (см. Команда Program -> Blank Chek (Программирование -> Проверить на чистоту)) ; • FLASH-память программируется и проверяется (см. Команда Program —> FLASH (Программирование -> FLASH)) ; • EEPROM-память программируется и проверяется (см. Команда Program EEPROM (Программирование -> EEPROM)); • Программируются Fuse- и Lock-биты микроконтроллера (см. Команда Program -> Lock Bits (Программирование Биты блокировки) и Команда Program -> Fuse Bit(s) (Программирование -> Биты-предохранители)).
3.2. Работа с проектами 139 Меню Read (Чтение) В состав меню Read (Чтение) входит несколько команд. В Табл. 3.9 даётся краткое описание. Таблица 3.9. Команды меню Read (Чтение) Команда Назначение FLASH Позволяет прочитать содержимое FLASH- памяти EEPROM Позволяет прочитать содержимое EEPROM-памяти Chip Signature (Сигнатура чипа) Позволяет прочитать сигнатуру чипа LockBit(s) (Блокировочные биты) Позволяет прочитать £осЛ-биты чипа Fuse Bit(s) (Биты-предохранители) Позволяет прочитать Же-биты чипа Calibration Byte(s) (Байты калибровки) Позволяет прочитать в чипе значение байтов калибровки внутреннего RC-генератора Programmer's Firmware Version (Версия микропрограмм программатора) Позволяет прочитать основную и дополнительную версии микропрограмм программаторов STK500/AVRISP/AVRProg Команда Read -> FLASH (Чтение -> FLASH) Эта команда позволяет прочитать содержимое FLASH-памяти чипа во FLASH-буфер. После выбора этой команды (Рис. 3.92) откроется окно Reading the FLASH to the Buffer (Чтение FLASH в буфер) с линейной диаграммой, отображающей про- цесс чтения (Рис. 3.93). CodeVisionAVR Chip Programmei Kanda Systems STK2O.. File Edit Program Chip Programming Options r FLASH Lock Bits— C No Protection FLASH \ EEPROM w Chip Signature % Lock Bits Fuse Bit(s) % Calibration Byte(s) Compare Help Chip: | ATmegal 6 FLASH.......... Start: 10 h Checksum: C000I FEOOh Read I Puc. 3.92. Выбор команды Read-* FLASH (Чтение -» FLASH). Puc. 3.93. Окно Reading the FLASH to the Buffer (Чтение FLASH в буфер) с линейной диаграммой.
140 Глава 3. Работа в IDE Code Vision A VR По завершении чтения это окно автоматически закроется, а во FLASH-буфере окажется содержимое FLASH-памяти чипа. Команда Read -> EEPROM (Чтение -»EEPROM) Эта команда позволяет прочитать содержимое EEPROM-памяти чипа в EEPROM-буфер. После выбора этой команды откроется окно Reading the EEPROM to the Buffer (Чтение EEPROM в буфер) с линейной диаграммой, отображающей процесс чте- ния (Рис. 3.94). Рис. 3.94. Окно Reading the EEPROM to the Buffer (Чтение EEPROM в буфер) с линейнойдиаграммой. По завершении чтения это окно автоматически закроется, а в EEPROM-буфе- ре окажется содержимое EEPROM-памяти чипа. Команда Read -э- Chip Signature (Чтение -> Сигнатура чипа) Эта команда позволяет прочитать сигнатуру чипа. Все микроконтроллеры AVR фирмы Atmel имеют трёхбайтовый сигнатурный код, по которому идентифицируется устройство. Эти три байта, размещённые в отдельном адресном пространстве, содержат следующую информацию: • ByteO — код производителя; • Bytel — объём FLASH-памяти; • Byte2 — тип микроконтроллера. Например, для AT90S8515 эти байты имеют следующие значения: • ByteO = OxlE (производитель Atmel); • Bytel = 0x93 (8 КБ FLASH-памяти); • ByteZ = 0x01 (тип AT90S8515). После выбора этой команды откроется окно Information (Информация) со значениями байтов сигнатуры (Chip Signature) и типом (Chip) используемого чипа (Рис. 3.95). Рис. 3.95. Окно Information (Информация) с сигнатурой и типом используемого чипа.
3.2. Работа с проектами 141 Если выбрана опция Check Signature (Проверка сигнатуры) (Рис. 4.96), то пе- ред каждой операцией будет считываться сигнатура чипа и по ней идентифици- роваться используемый чип. Рис. 3.96. Опция Check Signature (Проверка сигнатуры) выбрана. Если тип используемого микроконтроллера не совпадает с типом, выбранным в окошке Chip (Чип), то появится окно CodeVisionAVR с предупреждением (Рис. 3.97). Рис. 3.97. Окно CodeVisionAVR с предупреждением. В этом окне говорится следующее: Сигнатура чипа не совпадает Считано: ByteO = 1 Eh Bytel = 93h Byte2 = 06h Должно быть: ByteO = lEh Bytel = 93h Byte2 = Olh. Это позволяет избежать ошибок, которые могут привести к повреждению ис- пользуемого микроконтроллера. Подробнее о сигнатуре чипа читайте описание используемого микроконтрол- лера (см. Команда Help ~>AVR Data Sheets (Помощь -> Описания AVR)). Команда Read -> Lock Bits (Чтение -> Блокировочные биты) Эта команда позволяет прочитать ХосЛ-биты (Биты блокировки) чипа. После выбора этой команды откроется окно Information (Информация) со значениями ХосЛ-битов (Битов блокировки) используемого чипа (Рис. 3.98). Рис. 3.98. Окно Information (Информация) со значениями ХосЛ-битов (Битов блокировки) используемого чипа.
142 Глава 3. Работа в IDECodeVisionAVR Подробнее о £ос£-битах (Битах блокировки) чипа читайте описание исполь- зуемого микроконтроллера (см. Команда Help -» AVR Data Sheets (Помощь -> Опи- сания AVR)\ Команда Read -> Fuse Bit(s) (Чтение -> Биты-предохранители) Эта команда позволяет прочитать Ядо-биты (Биты-предохранители) чипа. После выбора этой команды откроется окно Information (Информация) со значениями Ядо-битов (Битов-предохранителей) используемого чипа (Рис. 3.99). Рис, 3.99. Окно Information (Информация) со значением fhse-битов (Битов-предохранителей) используемого чипа. Подробнее о Ядо-битах (Битах-предохранителях) чипа читайте описание на используемый микроконтроллер (см. Команда Help -> AVR Data Sheets (Помощь -> Описания AVR)). Команда Read -> Calibration Byte(s) (Чтение -» Байты калибровки) Эта команда позволяет прочитать в чипе байты калибровки внутреннего RC-генератора. После выбора этой команды откроется окно Information (Информация) со значениями байтов калибровки используемого чипа (Рис. 3.100). Рис. 3.100. Окно Information (Информация) со значением байтов калибровки используемого чипа.
3.2. Работа с проектами 143 Подробнее о байтах калибровки внутреннего RC-генератора чипа читайте описание используемого микроконтроллера (см. Команда Help AVR Data Sheets (Помощь -> Описания AVR)). Команда Read -> Programmer's Firmware Version (Чтение -> Версия микропрограмм программатора) Если выбран программатор Atmel STK500, AVRISP или AVRProg (прикладное описание AVR910), то дополнительно в меню Read (Чтение) представлена коман- да Read -» Programmer's firmware Version (Чтение -> Версия микропрограмм про- грамматора). Она позволяет прочитать основную и дополнительную версии мик- ропрограмм программаторов STK500/AVRISP/AVRProg. После выбора этой команды откроется окно Information (Информация) с но- мерами основной (Major) и дополнительной (Minor) версий микропрограмм вы- бранного программатора (Рис. 3.101). Рис. 3.101. Окно Information (Информация) с номерами основной (Major) и дополнительной (Minor) версий микропрограмм программатора STK500. Меню Compare (Сравнение) Для того чтобы вызвать выпадающее меню Compare (Сравнение), необходимо в строке меню щёлкнуть по названию Compare (Сравнение) (Рис. 3.102). В состав меню Compare (Сравнение) входит две команды. В Табл. 3.10 даётся их краткое описание. Таблица 3.10. Команды меню Compare (Сравнение) Команда Назначение FLASH Позволяет сравнить содержимое буфера FLASH-памяти с содержимым FLASH-памяти чипа EEPROM Позволяет сравнить содержимое буфера EEPROM-памяти с содержимым EEPROM-памяти чипа
144 Глава 3. Работа в IDE Code VisionA VR Рис. 3.102. Выпадающее меню Compare (Сравнение). Команда Compare FLASH (Сравнение FLASH) Эта команда позволяет сравнить содержимое буфера FLASH-памяти с содер- жимым FLASH -памяти чипа. После выбора этой команды (Рис. 3.103) откроется окно Comparing the FLASH with the Buffer (Сравнение FLASH с буфером) с линейной диаграммой, отобража- ющей процесс сравнения (Рис. 3.104). CodeVisionAVR Chip Programmer - Kanda Systems STK2O... 'годгат АН| Д/ Reset Chip: | АТ 90S 8515 г EEPROM- " h I Start: [o i Checksum: r Chip Programming Options FLASH Lock V r FLASH— i Start jo Checksum: Al Puc. 3.103. Выбор команды Compare -> FLASH (Сравнение -> FLASH).
3.2. Работа с проектами 145 Рис. 3.104. Окно Comparing the FLASH with the Buffer (Сравнение FLASH с буфером) с линейной диаграммой. Если будет обнаружено различие, то откроется окно Information (Информа- ция) с указанием найденных различий (Рис. 3.105). Рис. 3.105. Окно Information с указанием найденных различий. В этом окне говорится следующее: Содержимое FLASH не совпадает по адресу: 42h Из FLASH Считано: BFEEh Данные Буфера: OOOOh. Продолжить? Если щёлкнуть по кнопке Yes (Да), то проверка будет продолжена. Если щёлкнуть по кнопке No (Нет), то проверка будет прекращена. Если содержимое буфера FLASH-памяти совпадает с содержимым FLASH -памяти чипа, то никакое окно не выводится. Команда Compare EEPROM (Сравнение -> EEPROM) Эта команда позволяет сравнить содержимое буфера EEPROM-памяти с со- держимым EEPROM-памяти чипа. После выбора этой команды откроется окно Comparing the EEPROM with the Buffer (Сравнение EEPROM с буфером) с линейной диаграммой, отображающей процесс сравнения (Рис. 3.106). Рис. 3.106. Окно Comparing the EEPROM with the Buffer (Сравнение EEPROM с буфером) с линейной диаграммой. Если будет обнаружено различие, то откроется окно Information (Информа- ция) с указанием найденных различий (Рис. 3.107).
146 Глава 3. Работа в IDE Code VisionA VR Рис. 3.107. Окно Information (Информация) с указанием найденных различий. В этом окне говорится следующее. Содержимое EEPROM не совпадает по адресу: 85h Из EEPROM Считано: FFh Данные Буфера: AAh. Продолжить? Если щёлкнуть по кнопке Yes (Да), то проверка будет продолжена. Если щёлкнуть по кнопке No (Нет), то проверка будет прекращена. Если содержимое буфера flash-памяти совпадает с содержимым flash-памяти чипа, то никакое окно не выводится. Меню Help (Помощь) В этом меню всего одна команда Help (Помощь). При вызове меню Help (Помощь) появится окно CodeVisionAVR С Compiler Help (Помощь по компилятору Си CodeVisionAVR) с соответствующей темой по- мощи (Рис. 3.108). CodeVisionAVR С Compiler Help Файл Правка Закладка Параметры Справка Содержание Указатель Цазад Печать The AVR Chip Programmer The CodeVisionAVR IDE has a built-in In-System AVR Chip Programmer that lets you a easily transfer your compiled program to the microcontroller for testing. The Programmer is designed to work with the Atmel STK500/AVRISP/AVRProg (AVR910 application note), Kanda Systems STK200+/300, Dontronics DT006, Vogel Elektronik VTEC-ISP, Futurlec JRAVR or the MicroTronics ATCPU/Mega2000 development boards. The type of the used programmer and the printer port can be selected by using the Settings|Programmer menu command The AVR Chip Programmer is executed by selecting the Tools|Chip Programmer menu command or by pressing the Chip Programmer button on the toolbar. CodeVisionAVR Chip Programmer Kanda Systems STK200 + /300 Fie Edt Program Read Compare Help ДиМЯВИЯ! W I I*3! Doeol Phin I Puc. 3.108. Окно CodeVisionAVR C Compiler Help (Помощь по компилятору Си CodeVisionAVR) с соответствующей темой помощи.
3.2. Работа с проектами 147 Некоторые замечания Если программатор подключён неправильно, то при выборе команд из меню Program (Программирование), Read (Чтение) или Compare (Сравнение) появится окно CodeVisionAVR с предупреждением (Рис. 3.109). CodeVisionAVR Communication error, please check the following: - the Programmer's power supply is on - the Programmer type is correctly set in the Settings (Programmer menu - the Printer Port address is correctly set in the Settings (Programmer menu - the AVR chip is not damaged. | OK ........................................ .......•...........' " ........".... Puc. 3.109. Окно CodeVisionAVR с предупреждением. В этом окне говорится следующее. Ошибка связи, проверьте, пожалуйста, следующее: блок питания программатора включён тип программатора в меню Setting|Programmer установлен правильно адрес Порта принтера в меню Setting|Programmer установлен правильно чип AVR не поврежден. Для различных микроконтроллеров некоторые опции программирования мо- гут быть недоступны. Для правильной настройки опций программирования следует обратиться к описанию на соответствующий микроконтроллер (см. Команда Help AVR Data Sheets (Помощь Описания AVR)). 3.2.6. Закрытие проекта Для того чтобы прекратить работу с текущим проектом, следует использовать соответствующую команду (см. Команда File —> Close Project (Файл —> Закрыть Проект)). Если файлы проекта были изменены и до этого не сохранялись, то будет вы- ведено диалоговое окно Confirm (Подтверждение), показанное на Рис. ЗЛЮ. В этом окне будет спрошено, сохранять ли изменения в том или ином файле. Рис. 3.110. Диалоговое окно Confirm (Подтверждение). При сохранении проекта IDE CodeVisionAVR создаст резервный файл с рас- ширением .рг~.
148 Глава 3. Работа в IDE Code VisionA VR 3.2.7. Рекомендации При разработке нового проекта в IDE CodeVisionAVR можно воспользоваться автоматическим генератором программ CodeWizardAVR (см. Автоматический Программный Генератор CodeWizardAVR). При разработке нового проекта без использования CodeWizardAVR рекомен- дуется следующая последовательность действий: 1. Запустить CodeVisionAVR (см. Запуск CodeVisionAVR). 2. Создать новый проект. Для этого выбрать соответствующую команду (см. Команда File New (Файл -> Новый)) или щёлкнуть по соответствующей кнопке на панели инстру- ментов (Рис. 3.111). Рис. 3.111. Кнопка Create new file (Создать новый файл) на панели инструментов. В появившемся диалоговом окне Create New File (Создать новый файл) вы- брать тип (File ТУре) создаваемого файла Project (Проект) и щёлкнуть по кнопке ОК (Рис. 3.112). В появившемся диалоговом окне Confirm (Подтверждение), в котором будет спрошено, использовать ли для создания нового проекта CodeWizardAVR (Авто- матический генератор программ), щёлкнуть по кнопке No (Нет) (Рис. 3.113). Рис. 3.112. Диалоговое окно Create New File (Создать новый файл). Рис. 3.113. Диалоговое окно Confirm (Подтверждение).
3.2. Работа с проектами 149 В появившемся диалоговом окне Create New Project (Создание нового проек- та) определить местоположение нового проекта, его файловое имя и щёлкнуть по кнопке Сохранить (Рис. 3.114). Рис. 3.114. Диалоговое окно Create New Project (Создание нового проекта). Файл проекта будет иметь расширение .prj. В появившемся диалоговом окне Configure Project (Конфигурация проекта) с именем текущего проекта щёлкнуть по кнопке ОК (Рис. 3.115). Рис. 3.115. Диалоговое окно Configure Project (Конфигурация проекта) с именем текущего проекта.
150 Гшва 3. Работа в IDE Code VisionA VR При этом появится окно нового пустого проекта (Рис. 3.116). Рис. 3.116. Окно нового пустого проекта New.prj. 3. Создать главный текстовый файл проекта для набора исходного текста на Си. Только в этом файле ОБЯЗАТЕЛЬНО должна присутствовать функция main. Для этого выбрать соответствующую команду (см. Команда File -> New (Файл -> Новый)) или щёлкнуть по соответствующей кнопке на панели инструментов (Рис. 3.117). Рис. 3.117. Кнопка Create New File (Создать новый файл) на панели инструментов. В появившемся диалоговом окне Create New File (Создать новый файл) вы- брать тип (File ТУре) создаваемого файла Source (Исходник) и щёлкнуть по кнопке ОК (Рис. 3.118). Рис. 3.118. Диалоговое окно Create New File (Создать новый файл). f ile ||
3.2. Работа с проектами 151 При этом в области редактирования появится окно редактора для вновь соз- данного файла с именем untitled.c. (Рис. 3.119). Название нового файла появится в окне Navigator (Навигатор). Рис. 3.119. Новое окно редактора для файла untitled.c. 4. Переименовать созданный файл. Для этого выбрать соответствующую команду (см. Команда File -> Save As... (Файл Сохранить как...)), В появившемся диалоговом окне Save ... As (Сохранить ... как) ввести новое имя файла (не забудьте проверить его расширение) и щёлкнуть по кнопке Сохра- нить (Рис. 3.120). Рис. 3.120. Диалоговое окно Save ... As (Сохранить ... как). После этого файл будет переименован (Рис. 3.121).
152 Глава 3. Работа в IDE Code VisionA VR Рис. 3.121. Результат переименования файла 5. Добавить созданный файл в проект. Для этого выбрать соответствующую команду (см. Команда Project Configure (Проект Конфигурировать)) или щёлкнуть по соответствующей кнопке на па- нели инструментов (Рис. 3.122). Рис. 3.122. Кнопка Configure the Project (Конфигурировать проект) на панели инструментов. В появившемся диалоговом окне Configure Project (Конфигурация проекта) на закладке Files (Файлы) щёлкнуть по кнопке Add (Добавить) (Рис. 3.123). ч Configure Project New.prj Files jy Compiler | Alter Make | [5 D: \AVR T ool$\CodeVi$ion_1 _24_1 c\E xamplesKN ei Add source чк Puc. 3.123. Диалоговое окно Configure Project (Конфигурация проекта) с именем текущего проекта.
3.2. Работа с проектами 153 В появившемся диалоговом окне Add File То Project (Добавить файл к проекту) выбрать соответствующий файл и щёлкнуть по кнопке Открыть (Рис. 3.124). Рис. 3.124. Диалоговое окно Add File То Project (Добавить файл к проекту). После этого файл будет добавлен в проект. При этом в окне Navigator (Навига- тор) созданный файл переместится из ветви Other Files (Прочие файлы) в ветвь Project (Проект) (Рис. 3.125). CodeVisionAVR New.prj File Edit Project Tools Settings Windows Help Messages | Puc. 3.125. Файл new.c добавлен в проект. Navigator R CodeVisionAVR Й 0 Project: New H D:\AVRTools\CodeVision_1 _24_1 c\Examples\New\new.c 6. При необходимости, если проект будет содержать несколько файлов, пов- торить п. 3...5 для остальных файлов. 7. Сконфигурировать проект (см. Конфигурирование проекта). При этом на закладке С Compiler (Компилятор Си) следует выбрать микро- контроллер AVR, для которого разрабатывается проект и тактовая частота. Сле- дует также убедиться, что в опции File Output Format(s) (Формат(ы) выходных файлов) есть COF и HEX. Подробнее см. Закладка С Compiler (Компилятор Си). На закладке After Make (После построения) следует выбрать опцию Program the Chip (Запрограммировать чип) и настроить опции в поле Chip Programming
154 Глава 3. Работа в IDE Code VisionA VR Options (Опции программирования чипа). Подробнее см. Закладка After Make (После построения). 8. Ввести текст программы в окне вновь созданного файла в области редакти- рования (Рис. 3.126). Рис. 3.126. Окно редактора для файла new.c с текстом программы. 9. Сохранить все файлы проекта (см. Команда File -> Save All (Файл Сохра- нить всё)). 10. Скомпилировать проект (см. Компиляция проекта). 11. При необходимости устранить ошибки и повторить п. 9, 10. 12. Построить проект (см. Построение проекта). 13. При необходимости устранить ошибки и повторить п. 9... 12. 14. Отладить программу (см. Отладка программы). 15. При необходимости внести коррективы и повторить п. 9... 14. 16. Записать программу в микроконтроллер AVR. При этом можно воспользоваться кнопкой Program (Программировать) (см. Построение проекта) либо запустить программатор, выбрав соответствующую ко- манду (см. Команда Tools Chip Programmer (Инструменты -> Программатор чи- пов)), или щёлкнуть по соответствующей кнопке на панели инструментов (Рис. 3.127). Рис. 3.127. Кнопка Run the chip programmer (Запустить программатор чипов) на панели инструментов.
3.2. Работа с проектами 155 Подробнее об использовании программатора см. Запись программы в чип AVR. 17. Отладить разрабатываемый проект на аппаратном уровне, т. е. проверить работу микроконтроллера с записанной программой в разрабатываемом элект- ронном устройстве. 18. При необходимости внести коррективы в программу и повторить п. 9...17. 19. Закрыть проект (см. Закрытие проекта). 20. Выйти из IDE CodeVisionAVR (см. Команда File -> Exit (Файл -> Выход)).
ГЛАВА ОСНОВЫ ЯЗЫКА СИ В этой главе приводятся лишь начальные сведения о языке Си и, где необхо- димо, поясняются специфические особенности реализации языка компилятором Си CodeVisionAVR. В этой книге, кроме специально оговоренных случаев, для примеров подклю- чения внешних устройств будут использоваться микроконтроллеры AT90S2313, AT90S8515 и AT90S8535 в типовом включении (Рис. 4.1, 4.2 и 4.3 соответственно). VCC< Q1 Hah' • С1 27 нФ R1 10 кОм £ 7 2 С2 27 нФ 6 PD0/RXD PD1/TXD PD2/INT0 PD3/INT1 PD4/T0 PD5/T1 PD6/ICP S1 «RESET» XTAL1 XTAL2 RESET DD1 20 AT90S2313 PB0/AIN0 PB1/AIN1 РВ2 PB3/OC1 РВ4 PB5/MOSI PB6/MISO PB7/SCK сз 0.1 мкФ Рис. 4.1. Типовая схема включения микроконтроллера AT90S2313. 12 16 18
3.2. Работа с проектами 157 Q1 vcc< R1 10 кОм 19 18 DD1 40 AT90S8515 Но|- • С1 27 нФ Q1 С2 27 нФ S1 «RESET 13 77 16 77 XTAL1 XTAL2 RESET РВО/ТО РВ1/Г1 PB2/AIN0 PB3/AIN1 PB4/SS PB5/MOSI PB6/MISO PB7/SCK PDO/RXD PD1/TXD PD2/INT0 PD3/INT1 PD4 PD5/OC1A PD6/WR PD7/RD PAO/ADO PA1/AD1 PA2/AD2 PA3/AD3 PA4/AD4 PA5/AD5 PA6/AD6 PA7/AD7 РС0/А8 РС1/А9 РС2/А10 РСЗ/А11 РС4/А12 РС5/А13 РС6/А14 РС7/А15 ALE ОС1В ICP 39 77 37 35 77 33 32 21 23 77 26 77 77 29 VCC GND СЗ 0.1 мкФ Рис. 4.2. Типовая схема включения микроконтроллера AT90S8515. vcc< R1 10 кОм 12 DD1 10 AT90S8535 Но|-’ С1 27 нФ С2 27 нФ S1 «RESET 15 16 17 “77 20 21 XTAL1 XTAL2 RESET РВО/ТО РВ1/Т1 PB2/AIN0 PB3/AIN1 PB4/SS PB5/MOSI PB6/MISO PB7/SCK PA0/ADC0 PA1/ADC1 PA2/ADC2 PA3/ADC3 PA4/ADC4 PA5/ADC5 PA6/ADC6 PA7/ADC7 40 39 38 36 77 34 33 +5 В ОВ PD0/RXD PD1/TXD PD2/INT0 PD3/INT1 PD4/OC1B PD5/OC1A PD6/ICP о PD7/OC2 § РСО РС1 РС2 РСЗ РС4 РС5 PC6/TOSC1 PC7/TOSC2 AREF AGND AVCC 22 23 77 25 27 77 29 31 30 +5 В ОВ Рис. 4.3. Типовая схема включения микроконтроллера AT90S8535. На последующих схемах для упрощения чертежей эти цепи и элементы по- казаны не будут.
158 Глава 4. Основы языка Си 4.1. Препроцессор Препроцессор (макропроцессор) — это составная часть языка Си, которая об- рабатывает исходный текст программы до того, как он пройдет через компилятор. Препроцессор читает строки текста и выполняет действия, определяемые командными строками. Если первым символом в строке, отличным от пробела, является символ #, то такая строка рассматривается препроцессором как команд- ная. Командные строки называются директивами препроцессора. Директивы препроцессора позволяют: • включать в программу текст из других файлов; • передавать компилятору специальные директивы; • определять макросы, которые облегчают программирование и улучшают удобочитаемость исходного кода; • задавать условия компиляции для отладочных целей и/или для уменьше- ния размера получаемого кода. Препроцессор компилятора CodeVisionAVR имеет несколько директив. В Табл. 4.1 даётся их краткое описание. Таблица 4,1. Директивы препроцессора компилятора CodeVisionAVR Директива Назначение #include Используется для включения в программу другого файла #define Используется для замены одних лексических единиц языка Си на другие, а также для генерации макросов #undef Используется для отмены действия директивы #define #if Используются для условной компиляции #ifdef #ifndef #else #endif #line Используется для изменения встроенных макросов LINE и FILE (см. Встроенные макросы) #error Позволяет остановить компиляцию и отобразить сообщение об ошибках #asm Используются для включения в исходную программу ассемблерного кода #endasm #pragma... Разрешает специальные директивы компилятора ВСЕ директивы препроцессора начинаются со знака #. После директив препроцессора точка с запятой НЕ СТАВИТСЯ. 4.1.1. Директива #include Директива #include включает в программу содержимое указанного файла. Эта директива указывает препроцессору, что содержимое указанного файла надо вставить в том месте программы, где использована данная директива.
4.1. Препроцессор 159 Эта возможность препроцессора позволяет следовать в языке Си идеям структур- ного программирования, согласно которым большая программа обычно расчленяет- ся на логически завершённые части, и затем каждая оформляется как самостоятель- ная функция. После отладки каждая из них оформляется в виде отдельного файла и при необходимости включается в отлаживаемую программу директивой #include. Часто в таких файлах содержатся макроопределения, и после включения их в исход- ный модуль директивой #include они становятся доступными для всех функций. В CodeVisionAVR директивой #include можно вложить до 16 файлов. Директива #include имеет две синтаксические формы: #include "имя_файла" #include <имя_файла> Имя_файла может состоять либо только из имени файла, либо из имени файла с предшествующим ему маршрутом. Если имя файла задано в угловых скобках, то компилятор будет искать этот файл в директории, куда была установлена программа CodeVisionAVR, в подди- ректории ..\INC, а также в директориях, заданных в конфигурации проекта (см. Закладка С Compiler (Компилятор Си)). Если имя файла указано в кавычках, то компилятор будет искать этот файл в текущей директории проекта. Если его там нет, то поиск будет осуществлён в пап- ке /inc в директории компилятора, а также в директориях, заданных в конфигу- рации проекта (см. Закладка С Compiler (Компилятор Си)). Директива #include может быть вложенной, т. е. во включаемом файле тоже может содержаться директива #include, которая замещается после включения файла, содержащего эту директиву. Директива #include широко используется для включения в программу так на- зываемых заголовочных файлов (файлы с расширением .h), содержащих опреде- ления периферийных устройств и векторов прерываний используемого микро- контроллера, прототипы библиотечных функций, прототипы функций, опреде- лённых пользователем, и т. д. Поэтому большинство программ в CodeVisionAVR начинаются с этой директивы. Полезные макроопределения, используемые в программе, можно «замаски- ровать», поместив их в файл «имя_файла.Ь», а в начале исходного файла с текстом программы поместить командную строку: ^include "имя_файла.h". 4.1.2. Директивы #define, #undef Директива #define служит для замены часто использующихся одних лексичес- ких единиц языка Си (констант, ключевых слов, операторов или выражений) на другие, так называемые идентификаторы. Идентификаторы, заменяющие текс- товые или числовые константы, называют именованными константами. Иденти- фикаторы, заменяющие фрагменты программ, называют макроопределениями, причём макроопределения могут иметь аргументы. Директива #define имеет две синтаксические формы:
160 Глава 4. Основы языка Си ttdefine идентификатор текст ttdefine идентификатор (список параметров) текст Перед компиляцией программы препроцессор в соответствии с директивой #define заменит все идентификаторы, встречающиеся в программе, на соответству- ющий им текст. Такой процесс называется макроподстановкой. Текст может пред- ставлять собой любой фрагмент программы на Си, а также может и отсутствовать. В последнем случае все соответствующие идентификаторы удаляются из программы. Пример: ttdefine А 15 #define В (А+20) Эти директивы заменят в тексте программы каждый идентификатор А на чис- ло 15, а каждый идентификатор В на выражение (15+20) вместе с окружающими его скобками. Скобки, содержащиеся в макроопределении, позволяют избежать ошибок, связанных с порядком операций вычисления. Например, при отсутствии скобок выражение С = В*3 будет преобразовано в выражение С = 15+20*3, а не в выра- жение С=(15+20)*3, и в результате получится 75, а не 105. Во второй синтаксической форме в директиве #define имеется список фор- мальных параметров, который может содержать один или несколько идентифи- каторов, разделённых запятыми. Формальные параметры в тексте макроопреде- ления отмечают позиции, на которые должны быть подставлены фактические ар- гументы макровызова. Каждый формальный параметр может появиться в тексте макроопределения несколько раз. При макровызове вслед за идентификатором записывается список фактичес- ких аргументов, количество которых должно совпадать с количеством формаль- ных параметров. Пример: ttdefine X(a,b,c) ( (а) * (Ь) - (с) ) Препроцессор в соответствии с этой директивой заменит фрагмент Y=X(k+m,k-m,n); на фрагмент Y=((k+m)*(k-m)-(n)) ; Как и в предыдущем примере, круглые скобки, в которые заключены фор- мальные параметры макроопределения, позволяют избежать ошибок, связанных с неправильным порядком выполнения операций, если фактические аргументы являются выражениями. Чтобы преобразовать параметр макроопределения в символьную строку, мож- но использовать оператор #. Пример: ttdefine PRINT-STRING(х) printf(#х)
4.1. Препроцессор 161 Препроцессор в соответствии с этой директивой заменит фрагмент PRINT_STRING(Good); на фрагмент printf("Good"); Чтобы соединить два параметра в один, можно использовать оператор ##. Пример: ttdefine NAME(a,b) а ## b Препроцессор в соответствии с этой директивой заменит фрагмент char NAME(Hello,World)- 1; на фрагмент char HelloWorld = 1; Директиву #define можно перенести на новую строку, используя символ \. Пример: ^define STRING "Это очень \ длинная строка..." Макроопределения иногда используются вместо определений функций, обычно из соображений эффективности. Но следует помнить, что препроцессор может лишь автоматически заменять одну строку на другую, не разбираясь, зачем это нужно. В отличие от параметра функции параметр макроопределения вычис- ляется при каждом вхождении в макроопределение. Пример: ttdefine SQUARE(х) (х)*(х) void main(void) { int z=2; int Y=0; Y=SQUARE(+ + z); } В этом примере при первом вхождении в макрос значение z будет равно 3 (++z означает увеличить z на единицу), а при втором вхождении z будет равно 4. В результате Y получится равным 3x4= 12. При определении SQUARE(x) как функции: // объявление функции int SQUARE(int х) ; // определение функции int SQUARE(int х) { return (х)*(х) ;
162 Глава 4. Основы языка Си и при последующем её вызове: int Y; //объявление переменной Y как целой int z=2; //объявление переменной z как целой //и присвоение ей значения 2 Y=SQUARE(++z);//вычисление значения Y //с помощью вызова функции SQUARE Значение Y получится равным 9, так как сначала вычисляется аргумент z (он будет равен 3, так как z = 2, a ++z = 3), а затем с этим значением аргумента вы- числяется значение функции SQUARE. Директива #undef используется для отмены действия директивы #define. Синтаксис этой директивы: #undef идентификатор Директива отменяет действие текущего определения #define для указанного идентификатора. Не является ошибкой использование директивы #undef для идентификатора, который не был определён директивой #define. Пример: #undef А ttundef SQUARE Эти директивы отменяют определение именованной константы А и макрооп- ределения SQUARE. 4.1.3. Директивы #if, #ifdef, #ifndef, #else и #endif Директивы #if, #ifdef, #iftidef, #else и #endif можно использовать для условной компиляции. Условные конструкции препроцессора позволяют компилировать или пропускать часть программы в зависимости от выполнения некоторого усло- вия. Условие может принимать одну из описываемых ниже форм. #if константное_выражение Проверяется значение выражения, составленного из констант, и, если оно не равно нулю, компилируется (включается) последующий текст. #ifdef идентификатор Последующий текст компилируется, если «идентификатор» уже был опреде- лён для препроцессора директивой #define. #ifndef идентификатор Последующий текст компилируется, если «идентификатор» в данный момент не определён. За любой из трёх условных команд может следовать произвольное число строк текста, содержащих, возможно, команду вида #else и заканчивающихся #endif. Ес-
4.1. Препроцессор 163 ли проверяемое условие справедливо, то строки между #else и #endif игнорируют- ся. Если же проверяемое условие не выполняется, то игнорируются все строки между проверкой и командой #else, а если её нет, то командой #endif. Пример: Ш А=15 [группа операторов 1] #else [группа операторов 2] ttendif Если выражение А = 15 истинно, то будет компилирована группа операторов 1. В противном случае будет компилирована группа операторов 2. Пример: ttifdef NAME [группа операторов 1] #else [группа операторов 2] ttendif Если NAME (имя макроса) определено ранее директивой #define , то выраже- ние #ifdef будет истинным, и будет компилирована группа операторов 1. В противном случае, если NAME (имя макроса) ранее не определено или его определение отменено директивой #undef, будет компилирована группа операто- ров 2. Пример: ttifndef NAME [группа операторов 1] #else [группа операторов 2] ttendif Если NAME (имя макроса) ранее не определено или его определение отмене- но директивой #undef, то выражение #iftidef будет истинным и будет компилиро- вана группа операторов 1. В противном случае, если NAME (имя макроса) определено ранее директивой #define, будет компилирована группа операторов 2. Во всех трёх примерах директива #else и группа операторов 2 опциональны (не обязательны). 4.1.4. Директива #line Директиву #line можно использовать, чтобы изменить встроенные макросы __LINE___и____FILE___(см. Встроенные макросы). Её синтаксис: #line целая_константа ["имя_файла"]
164 Глава 4. Основы языка Си Пример: /* Установим встроенный макрос _LINE_ в 25, а ___FILE_ в "test.с" */ #line 25 "test.с" /* Установим встроенный макрос _LINE__ в 36 */ #line 36 Следует отметить, что эта директива изменяет текущее значение встроенного макроса ___LINE___, при этом с каждой последующей строкой значение макроса увеличивается на единицу. Пример: В этом примере в левом столбце приведены номера строк: 10: int Y=0; // Объявляем и сразу инициализируем переменную Y // Здесь __LINE_ равен 10 11: #line 50 // Присваиваем встроенному макросу _LINE__ значение 50 12: Y=__LINE_; // Присваиваем переменной Y значение макроса _LINE__, II который здесь уже равен 51 Таким образом, после выполнения 12-й строки программы значение Y будет равно 51. 4.1.5. Директива #еггог Директиву #еггог можно использовать, чтобы остановить компиляцию и отобразить сообщение об ошибках. Ее синтаксис: #еггог сообщение Пример: terror Здесь ошибка! //В этом месте компиляция будет остановлена и будет // выдано сообщение: "Здесь ошибка!" 4.1.6. Директивы #asm и #endasm Директивы #asm и #endasm можно использовать для включения в любом месте исходной программы ассемблерного кода. Директива #asm говорит компилятору о начале ассемблерного кода, а дирек- тива #endasm — о его завершении. Пример: /* Вставка ассемблерного кода */ void main(void) { #asm // начало ассемблерного кода sei ; разрешаем глобальные прерывания ttendasm // окончание ассемблерного кода }
4.1. Препроцессор 165 Обратите внимание, что комментарии в ассемблерной части кода отде- ляются точкой с запятой. Иначе будет выдаваться ошибка при ассембли- ровании (при построении проекта). Также можно использовать ассемблерную строку. Пример: #asm("sei") // разрешаем глобальные прерывания 4.1.7. Директивы #pragma Директивы #pragma... разрешают специальные директивы компилятора. В Табл. 4.2 даётся их краткое описание. Таблица 4,2, Директивы #pragma... препроцессора компилятора CodeVisionAVR Директива Назначение #pragma glbdef+ Используется для совместимости с проектами, созданными версиями CodeVisionAVR до VI .0.2.2 #pragma library Используется для определения необходимости компилировать/подсоединить определённый библиотечный файл #pragma opt Включает/выключает кодовый оптимизатор компилятора #pragma optsize При включённом кодовом оптимизаторе включает/выключает оптимизацию по скорости #pragma promotechar Включает/выключает перевод операндов из ANSI символов (char) в целое (int) #pragma regalloc Включает/выключает автоматическое распределение глобальных переменных в регистрах #pragma ruslcd Включает/выключает автоматическую перекодировку символов кириллицы при их выводе на модуль LCD #pragma savereg Включает/выключает автоматическое сохранение и восстановление регистров RO, Rl, R15, R22...R27, R30, R31 и SREG #pragma uchar Включает/выключает обработку символа (char) по умолчанию как беззнакового 8-битного #pragma used Разрешает/запрещает предупреждения компилятора о том, что функция была объявлена, но в программе не используется #pragma warn Разрешает/запрещает предупреждения компилятора #pragma 8bit_enums Разрешает/запрещает компилятору обрабатывать перечисления, как 8-битовый тип данных char (символ) Директива #pragma glbdef+ Директива #pragma glbdef+ используется для совместимости с проектами, соз- данными версиями CodeVisionAVR до VI.0.2.2. В этих версиях была разрешена опция Project —> Configure —> С Compiler —> Global #define (Проект —> Конфигура- ция -> Компилятор Си -> Глобальное определение). Эта директива говорит ком- пилятору о том, что макросы глобально видимы во всех программных модулях проекта. Эта директива должна быть установлена в начале первого исходного файла проекта. По умолчанию эта директива не активна, так что макросы видимы толь- ко в том программном модуле, где они определены.
166 Глава 4. Основы языка Си Директива #pragma library Директиву #pragma library можно использовать для определения необходи- мости компилировать/подсоединить определённый библиотечный файл. Пример: ttpragma library lib.lib // Подсоединить библиотечный файл lib.lib Директива #pragma opt Директиву #pragma opt можно использовать, чтобы включить или выключить оптимизатор кода компилятора. Эта директива должна быть установлена в начале исходного файла. По умолчанию оптимизация включена. Пример: #pragma opt- // Эта директива отключает оптимизацию ИЛИ ttpragma opt + // Эта директива включает оптимизацию Директива #pragma optsize Директиву #pragma optsize можно использовать, чтобы оптимизировать неко- торые части или всю программу по размеру получаемого кода или по скорости выполнения. Эту директиву можно применять, только если разрешён оптимиза- тор кода компилятора (см. Директива ttpragma opt). Пример: #pragma optsize+ [группа операторов 1] #pragma optsize- // Следующая часть программы будет оптимизирована //по минимальному размеру получаемого кода // Теперь программа будет оптимизирована //по максимальной скорости выполнения [группа операторов 2] По умолчанию выбор критерия оптимизации определяется в опции Optimize for (Оптимизировать по) при конфигурировании проекта (см. Закладка С Compiler (Компилятор Си)). Директива #pragma promotechar Директиву #pragma promotechar можно использовать, чтобы включить или вы- ключить перевод операндов из ANSI символов (char) в целые (int). Пример: #pragma promotechar+ // Включим перевод ANSI символа в целое ИЛИ #pragma promotechar- // Выключим перевод ANSI символа в целое
4.1. Препроцессор 167 По умолчанию перевод операндов определяется настройкой опции Promote char to int (переводить символ в целое) при конфигурировании проекта (см. За- кладка С Compiler (Компилятор Си)). Директива #pragma regalloc Директиву #pragma regalloc можно использовать, чтобы включить или выклю- чить автоматическое распределение глобальных переменных в регистрах. По умолчанию автоматическое распределение определяется настройкой оп- ции Automatic Register Allocation (Автоматическое распределение регистров) при конфигурировании проекта (см. Закладка С Compiler (Компилятор Си)). Пример: #pragma regalloc+ int X ^pragma regalloc- int Y // Включим автоматическое распределение в регистрах // глобальная переменная X будет // размещена в регистре // Выключим автоматическое распределение в регистрах // глобальная переменная Y не будет автоматически // размещена в регистре и, как правило, будет // размещена в SRAM Директива #pragma ruslcd Директиву #pragma ruslcd можно использовать, чтобы включить или выклю- чить автоматическую перекодировку символов кириллицы при их выводе на мо- дуль LCD. Разумеется, знакогенератор LCD должен иметь русские буквы. Под- робнее см. LCD-функции. Пример: ttpragma ruslcd+ // Включим автоматическую перекодировку символов кириллицы ИЛИ ttpragma ruslcd- // Выключим автоматическую перекодировку символов кириллицы По умолчанию автоматическая перекодировка символов кириллицы вы- ключена. Директива #pragma savereg Директиву #pragma savereg можно использовать, чтобы включить или выклю- чить автоматическое сохранение и восстановление регистров RO, Rl, R15, R22...R27, R30, R31 и SREG в течение прерываний. Пример: /* Директива ^pragma savereg */ /* Микроконтроллер AT90S2313 */ ttpragma savereg- // Выключим автоматическое сохранение регистров /* Функция обработки прерывания по переполнению таймера/счётчикаО */
168 Глава 4. Основы языка Си interrupt [7] void overflow_timerO(void) { /* теперь сохраним только регистры, которые используются программами в обработчике прерывания, например R22, R23 и SREG */ /* Этот кусок написан на ассемблере */ #asm push г22 push г23 in r22,SREG push r22 ttendasm /* Здесь располагается код Си обработчика прерывания */ /* теперь восстановим SREG, R23 и R22 */ /* Этот кусок написан на ассемблере */ #asm pop r22 out SREG,r22 pop r23 pop r22 ttendasm } /* снова разрешим автоматическое сохранение и восстановление регистров для других обработчиков прерываний */ tfpragma savereg+ По умолчанию автоматическое сохранение регистров в течение прерыва- ний включено. Директива #pragma uchar Директиву #pragma uchar можно использовать, чтобы включить или выклю- чить обработку символа (char) по умолчанию как беззнакового 8-битного. Пример: ttpragma uchar+ // Символ (char) по умолчанию будет беззнаковым ИЛИ #pragma uchar- // Символ (char) по умолчанию будет со знаком По умолчанию обработка символа определяется настройкой опции char is unsigned (символ является беззнаковым) при конфигурировании проекта (см. За- кладка С Compiler (Компилятор Си)). Директива #pragma used Директиву #pragma used можно использовать, чтобы разрешить или запретить предупреждения компилятора о том, что функция объявлена, но в программе не используется.
4.1. Препроцессор 169 Пример: ttpragma used+ // Эта директива запрещает компилятору // выдавать предупреждения о том, что функция // объявлена, но в программе не используется. // прототипы библиотечных функций int sum(int a, int b); int mulfint a, int b) ; tfpragma used- // Эта директива разрешает компилятору // выдавать предупреждения о том, что функция // объявлена, но в программе не используется. [группа операторов] По умолчанию генерация предупреждений о том, что функция объявлена, но в программе не используется, РАЗРЕШЕНА. Директива #pragma warn Директиву #pragma warn можно использовать, чтобы разрешить или запретить предупреждения компилятора. Пример: tfpragma warn- // Эта директива запрещает компилятору // выдавать предупреждения [группа операторов 1] tfpragma warn+ // Эта директива разрешает компилятору // выдавать предупреждения [группа операторов 2] По умолчанию генерация предупреждений компилятора определяется на- стройкой опции Enable Warnings (Разрешить предупреждения) при конфигуриро- вании проекта (см. Закладка С Compiler (Компилятор Си)). Директива #pragma 8bit_enums Директиву #pragma 8bit_enums можно использовать, чтобы разрешить или за- претить компилятору обрабатывать перечисления (enums), как 8-битные данные типа char (символ). Разрешение приводит к уменьшению размера кода и увеличению скорости выполнения скомпилированной программы. При запрещении перечисления считаются 16-битными данные типа int (це- лый), как требует стандарт ANSI. Пример: tfpragma 8bit_enums- // Запретить компилятору обрабатывать перечисления, // как 8-битовые данные типа char (символ) ИЛИ ttpragma 8bit_enums+ // Разрешить компилятору обрабатывать перечисления, // как 8-битовые данные типа char (символ)
170 Глава 4. Основы языка Си По умолчанию обработка перечислений определяется настройкой опции 8 bit enums (8-битовые перечисления) при конфигурировании проекта (см. Закладка С Compiler (Компилятор Си)). 4.1.8. Встроенные макросы В CodeVisionAVR есть встроенные макросы. В Табл. 4.3 перечислены эти мак- росы и дано их краткое описание. Таблица 4.3. Встроенные макросы компилятора CodeVisionAVR Макрос Описание _СН1Р_АТХХХХХ_ АТХХХХХ — выбранный тип чипа —CODEVISIONAVR— Версия компилятора _DATE_ Текущая дата в формате ттт ddyyyy _FILE_ Текущий скомпилированный файл _LINE_ Текущий номер строки скомпилированного файла _MCU_CLOCK_FREQUENCY_ Заданная тактовая частота чипа AVR _MODEL_SMALL_ Модель памяти SMALL _MODEL_TINY_ Модель памяти TINY -OPTIMIZE-SIZE_ Оптимизация по размеру -OPTIMIZE-SPEED- Оптимизация по скорости _TIME_ Текущее время в формате hh:mm:ss -UNSIGNED-CHAR_ Символ (char) по умолчанию беззнаковый -8BIT-ENUMS_ 8-битовые перечисления Макрос _СН1Р_АТХХХХХ_ Макрос -CHIP-ATXXXXX- будет определён, если АТХХХХХ, записанный буквами верхнего регистра, совпадает с типом чипа, определённым в опции Chip (Чип) при конфигурировании проекта (см. Закладка С Compiler (Компиля- тор Си)). Этот макрос можно использовать для условной компиляции. Пример: #ifdef _CHIP_AT90S8535_ [группа операторов 1] #else #ifdef _CHIP_AT90S8515_ [группа операторов 2] #endif [группа операторов 3] tfendif Если при конфигурировании проекта выбран чип типа AT90S8535, то будет оп- ределён макрос _CHIP_AT90S8535_ и компилирована группа операторов 1.
4.1, Препроцессор 171 Если при конфигурировании проекта выбран чип типа AT90S8515, то будет оп- ределён макрос _CHIP_AT90S8515 и компилирована группа операторов 2. Если при конфигурировании проекта выбран чип, отличный от AT90S8535 и AT90S8515, то оба макроса не будут определены и будет компилирована группа операторов 3. Макрос —CODEVISIONAVR— Этот макрос представляет собой номер версии компилятора. Он представля- ется как целое (integer); например, для V1.24.1x Standart он будет равен 1241. Пример: int Y=_CODEVISIONAVR__ // В этой версии компилятора переменной Y // присваивается значение 1241 Макрос —DATE— Этот макрос представляет собой текущую дату компиляции файла. Он пред- ставляется как строка символов в формате ттт dd уууу, где ттт — месяц (три первые буквы), dd — день, уууу — год. Например: 12 июля 2004 года будет пред- ставлено как Jul 12 2004. Пример: char string [] =_DATE_; // Присваиваем строке string[] значение // "гтатап dd уууу", где питан dd уууу текущая // дата компиляции файла Макрос— FILE— Этот макрос представляет собой название скомпилированного файла. Он представляется как строка символов. Пример: char string[]=___FILE_; // Присваиваем строке string[] значение // ""file_name.c", где file_name.c имя // скомпилированного файла Значение этого макроса можно изменить (см. Директива #line). Макрос—LINE— Этот макрос представляет собой текущий номер строки скомпилированного файла. Он представляется как целое (integer). Пример: В этом примере в левом столбце приведены номера строк: 11: int Y=0; // Объявляем и сразу инициализируем переменную Y 12: Y= LINE ; // Присваиваем переменной Y значение макроса LINE , // который здесь равен 12 (номер текущей строки)
172 Глава 4. Основы языка Си Значение этого макроса можно изменить (см. Директива #Нпе). Макрос _MCU_CLOCK_FREQUENCY_ Этот макрос представляет собой значение тактовой частоты чипа AVR, кото- рая определена в опции Clock (Тактовая частота) при конфигурировании проекта (см. Закладка С Compiler (Компилятор Си)). Значение выражается в герцах. Мак- рос представляется как целое (integer). Пример: /* При заданной тактовой частоте 4 МГц переменная Q будет иметь значение 4000000 */ long int Q; // Объявляем переменную Q Q =_MCU_CLOCK_FREQUENCY_; // Присваиваем переменной Q значение // макроса _MCU_CLOCK_FREQUENCY_ Макросы _MODEL_TINY_ и _MODEL_SMALL_ В зависимости от того, какая модель памяти выбрана в опции Memory Model (Модель памяти) при конфигурировании проекта (см. Закладка С Compiler (Ком- пилятор Си)), будет определён соответствующий макрос. Если выбрана модель памяти Tiny, то будет определён макрос _MODEL_TINY_. Если выбрана модель памяти Small, то будет определён макрос _MODEL_SMALL_. Эти макросы можно использовать для условной компиляции. Пример: tfifdef _MODEL_TINY_ [группа операторов 1] #else [группа операторов 2] #endif Если при конфигурировании проекта выбран тип памяти Tiny, то выражение #ifdef будет истинным и будет компилирована группа операторов 1. Если при конфигурировании проекта выбран тип памяти Small, то будет ком- пилирована группа операторов 2. Макросы _OPTIMIZE_SIZE_ и _OPTIMIZE_SPEED_ В зависимости от того, какой способ оптимизации выбран в опции Optimize for (Оптимизировать по) при конфигурировании проекта (см. Закладка С Compiler (Компилятор Си)), будет определён соответствующий макрос. Если выбрана оптимизация по размеру, то будет определён макрос -OPTIMIZE-SIZE-. Если выбрана оптимизация по скорости выполнения, то будет определён макрос -OPTIMIZE-SPEED-. Эти макросы можно использовать для условной компиляции.
4.1. Препроцессор 173 Пример: Hfdef .OPTIMIZE.SIZE_ [группа операторов 1] #else [группа операторов 2] #endif Если при конфигурировании проекта выбрана оптимизация по размеру, то выражение #ifdef будет истинным и будет компилирована группа операторов 1. Если при конфигурировании проекта выбрана оптимизация по скорости, то будет компилирована группа операторов 2. Макрос __Т1МЕ__ Этот макрос представляет собой текущее время компиляции файла. Он пред- ставляется как строка символов в формате hh:mm:ss, где hh — значение часов, тт — значение минут, ss — значение секунд. Пример: char string[]=_TIME__; // Присваиваем строке string[] значение // "hh : mm : ss", где hh : mm : ss - текущее время // компиляции файла Макрос _UNSIGNED_CHAR_ Этот макрос будет определён, если при конфигурировании проекта (см. За- кладка С Compiler (Компилятор Си)) разрешена опция char is unsigned (символ без знака) или использована директива #pragma uchar+ (см. Директива ttpragma uchar). Этот макрос можно использовать для условной компиляции. Пример: Hfdef .UNSIGNED.CHAR. [группа операторов 1] #else [группа операторов 2] #endif Если при конфигурировании проекта разрешена опция char is unsigned (сим- вол без знака) или ранее была использована директива #pragma uchar+, то выра- жение #ifdef будет истинным и будет компилирована группа операторов 1. В противном случае будет компилирована группа операторов 2. Макрос _8BIT_ENUMS_ Этот макрос будет определён, если при конфигурировании проекта (см. За- кладка С Compiler (Компилятор Си)) разрешена опция 8 bit enums (8-битовые пере- числения) или использована директива #pragma 8bit_enums+ (см. Директива ttpragma 8bit_enums).
174 Глава 4. Основы языка Си Этот макрос можно использовать для условной компиляции. Пример: #ifdef _8BIT_ENUMS_ [группа операторов 1] #else [группа операторов 2] #endif Если при конфигурировании проекта разрешена опция 8 bit enums (8-битовое перечисление) или ранее использована директива #pragma 8bit_enums+, то выра- жение #ifdef будет истинным и будет компилирована группа операторов 1. В противном случае будет компилирована группа операторов 2. 4.2. Зарезервированные ключевые слова Следующий список ключевых слов зарезервирован компилятором. Эти слова НЕ МОГУТ использоваться как имена идентификаторов. break flash eeprom sfrw bit float else static case for enum struct char funcused extern switch const goto register typedef continue if return union default inline short unsigned defined int signed void do interrupt sizeof volatile double long sfrb while 4.3. Идентификаторы Идентификатором является имя, которое даётся переменной, функции, мет- ке или другому объекту. Идентификатор может содержать буквы латинского ал- фавита (A...Z, a...z) и цифры (0...9), а также символ подчеркивания (_), который считается буквой. ИДЕНТИФИКАТОР может начинаться ТОЛЬКО с буквы или со знака подчеркивания. При написании идентификатора имеет значение регистр, т. е. Temp, temp и TEMP будут восприниматься компилятором CodeVisionAVR как различные иден- тификаторы. Для компилятора CodeVisionAVR максимальная длина идентификатора — 31 символ.
4.4. Комментарии 175 В качестве идентификатора нельзя использовать зарезервированные ключе- вые слова (см. Зарезервированные ключевые слова), а также имена функций библи- отеки компилятора. 4.4. Комментарии Комментарий — это набор символов, который компилятором игнорируется. Однако на этот набор символов накладываются следующие ограничения. Внутри набора символов, который представляет комментарий, не может быть специаль- ных символов, определяющих начало (/*) и конец (*/). Комментарии могут зани- мать как одну строку, так и несколько. Пример: /* комментарии к программе */ /* начало алгоритма */ ИЛИ /* комментарии можно записать в следующем виде, однако следует быть осторожным, чтобы внутри последовательности, которая компилятором игнорируется, не попались операторы программы, которые также будут игнорироваться */ Неправильное определение комментариев: /* комментарии к программе /* начало алгоритма */ */ ИЛИ /* комментарии к программе */ начало алгоритма */ Однострочный комментарий можно также определить, используя строку «//». Пример: // Это тоже комментарий ИЛИ char NAME=1; // комментарии на одной строке с оператором Вложенные комментарии НЕ ДОПУСКАЮТСЯ. 4.5. Константы Константами называются перечисления величин в программе. В отличие от переменных такие имена нельзя изменять. В языке Си разделяют четыре типа констант: целые константы, константы с плавающей точкой, символьные константы и строковые литералы. Целая константа — это десятичное, двоичное, восьмеричное или шестнадца- теричное число, которое представляет целую величину в одной из следующих форм: десятичной, двоичной, восьмеричной или шестнадцатеричной.
176 Глава 4. Основы языка Си Десятичная константа состоит из одной или нескольких десятичных цифр, причём первая цифра не должна быть нулем (в противном случае число будет вос- принято как восьмеричное). Например, 9876. Двоичная константа начинается с обязательной последовательности ОЬ и со- держит одну или несколько двоичных цифр (цифры двоичной системы счисле- ния: 0 или 1). Например, Obi 1100. Восьмеричная константа начинается с обязательного префикса 0 и содержит одну или несколько восьмеричных цифр (среди цифр должны отсутствовать восьмерка и девятка, так как эти цифры не входят в восьмеричную систему счис- ления). Например, 0123. Шестнадцатеричная константа начинается с обязательной последователь- ности Ох или ОХ и содержит одну или несколько шестнадцатеричных цифр (циф- ры, представляющие собой набор цифр шеснадцатеричной системы счисления: O...9,A,B,C,D,E,F). Например, Охеа или ОХЕА. Если требуется сформировать отрицательную целую константу, то перед запи- сью константы используют знак «—» (который называется унарным минусом). Например, —0х2А, —088, —16, —0Ы0. Каждой целой константе присваивается тип, определяющий преобразования, которые должны быть выполнены, если константа используется в выражениях. Тип константы зависит от её значения (см. Табл. 4.4). Для того чтобы любую целую константу определить типом long integer (длин- ная целая), достаточно в конце константы поставить суффикс «1» или «Ь». Напри- мер: 121, 076L, 0x3FAL, OblOlL. Для того чтобы любую целую константу определить типом unsigned integer (це- лые без знака), достаточно в конце константы поставить суффикс «и». Например: 12U, 023U, OxACU, 0Ы0Ш. Для того чтобы любую целую константу определить типом unsigned long integer (длинная целая без знака), достаточно в конце константы поставить суффикс «UL». Например: 12UL, 023UL, OxACUL, OblOlUL. Константа с плавающей точкой (floatingpoint) — десятичное число, представ- ленное в виде действительной величины с десятичной точкой или экспонентой. Формат имеет вид [ цифры ].[ цифры ] [ ЕIе [ +1 -] цифры ] Число с плавающей точкой состоит из целой и дробной части и/или экспо- ненты. Константы с плавающей точкой представляют положительные величины удвоенной точности (имеют тип double). Для определения отрицательной величи- ны следует сформировать константное выражение, состоящее из знака минуса и положительной константы. Например: -1.6Е-3, -0.072, -0.16Е5. Для того чтобы константу определить типом floating point (с плавающей точ- кой), достаточно в конце константы поставить суффикс «F». Например: 9.876F. Символьная константа (Character) — представляется символом, заключённым в одиночные кавычки. Значением символьной константы является числовой код символа. Например: ’g’.
4.5. Константы 177 Строковая константа (String), или литерал, — последовательность символов (включая строковые и прописные буквы русского и латинского алфавита, а также цифры), заключённых в кавычки («...»). Например: «Сидоров А.Г.», «город Моск- ва», «АБВГ1234», «»(пустая строка). В конец каждого строкового литерала компилятором добавляется нулевой символ. Здесь и далее в данной книге выражение «нулевой символ» подразумевает символ с кодом 0x00 (управляющая последовательность '\0'), а не символ нуля! Символ нуля имеет код 0x30. Строковый литерал имеет тип char[]. Это означает, что строка рассматривает- ся как массив символов. Отметим важную особенность: число элементов массива равно числу символов в строке плюс 1, так как нулевой символ (символ конца строки) также является элементом массива. Если установить строку в кавычках как параметр функции, эта строка автома- тически будет рассматриваться как константа и будет располагаться во FLASH-памяти. Пример: /* Определим две функции: memory_sram и memory_flash */ /* Функция memory_sram в качестве формального параметра использует указатель а на объект типа char, расположенный в SRAM */ void memory_sram (char *a) { [группа операторов 1] } /* Функция memory—flash в качестве формального параметра использует указатель а на объект типа char, расположенный во FLASH. На это указывает ключевое слово flash */ void memory_flash (char flash *a) { [группа операторов 2] } /* Здесь основная функция программы main */ void main(void) { /* Здесь будет ошибка! Поскольку функция адресует строку как расположенную в SRAM, но строка "Example", помещённая в кавычках, рассматривается компилятором как константа и автоматически будет расположена во FLASH */ memory_sram ("Example"); /* А здесь ошибки не будет! Поскольку функция адресует строку как расположенную во FLASH */ memory_flash ("Example"); } Константы могут быть сгруппированы в массивы, которые в CodeVisionAVR могут иметь вплоть до 8 измерений.
178 Глава 4. Основы языка Си Чтобы определить константы, хранящиеся во FLASH-памяти, следует ис- пользовать ключевые слова flash или const. Чтобы определить константы, хранящиеся в EEPROM, следует использовать ключевое слово еергош. Чтобы определить константы, хранящиеся в регистрах, следует использовать ключевое слово register. Константные выражения автоматически определяются в течение компиля- ции. Пример: /* Эти константы будут расположены в EEPROM */ eeprom int int_constl=12-7; // Целая константа int_constl будет равна 5 eeprom char char_const='b'; // Символьная константа char_const будет равна // числовому коду ASCII символа 'Ь', т. е. 0x62 /* Эти константы будут расположены во FLASH-памяти */ flash long long_int_const=136L; // Целая длинная константа // long_int_const будет равна 136 flash int int_array[10]={9,16}; // Этот целочисленный массив // имеет 10 членов, причём первые два // это 9 и 16, а остальные нули const int int_const2=0123; // Целая константа int_const2 будет равна 123 // в восьмиричном или 83 в десятичном виде /* Эта константа будет расположена в регистре */ register int int_const3=0xl0; // Целая константа int_const3 будет равна // 16 в десятичном или 10 в // шестнадцатеричном виде /* Эти константы будут расположены в SRAM */ int int_const4=0bl0; char string_const[]="Hello!"; // Целая константа int_const4 будет равна // 2 в десятичном или 10 в двоичном виде // Это строковая константа Константы НЕ МОГУТ быть объявлены в функциях. 4.6. Переменные В отличие от констант переменные во время выполнения программы могут множество раз изменять свои значения. Переменные могут быть глобальными или локальными. Глобальные переменные — это переменные, доступные во всех функциях про- граммы. Если глобальные переменные специально не инициализированы, то при запуске программы они автоматически устанавливаются в 0.
4,6. Переменные 179 Локальные переменные — это переменные, доступные только в функциях, где они объявлены. Локальные переменные при вызове функции автоматически не инициализируются и могут содержать всякий мусор. Синтаксис объявления переменных: [<место хранения>] «определение типа> <идентификатор>; Пример: /* Объявление глобальных переменных */ long х; char у; /* Инициализация глобальных переменных */ х=10; /* Объявление с одновременной инициализацией глобальных переменных */ int c=0xfa; void main(void) { /* Объявление локальных переменных, т. е. переменных только внутри этой функции */ int а; long b; /* Инициализация локальных переменных */ а=0х2с; /* Объявление с одновременной инициализацией локальных переменных */ char с='s'; } Обычную локальную переменную видно только в той функции, в которой она объявлена. Её значение хранится до тех пор, пока выполняется эта функция. Чтобы обеспечить возможность хранить значение локальной переменной при выходе из функции и использовать это значение при повторном вызове этой фун- кции, локальные переменные должны быть объявлены со спецификатором клас- са памяти static (статические). Такая переменная имеет глобальное время жизни и область видимости внутри функции, в которой она объявлена. При выполнении программы статические переменные инициализируются только один раз, даже если функция, где они инициализируются, вызывается многократно. Пример: * Объявления локальных переменных х с классом памяти static */ int functionl(void) { static int x=2; return (x=x+5); } 11 II определение функции functionl // с локальной переменной x, с классом // памяти static //
180 Глава 4. Основы языка Си int function2(void) // { // определение функции function2 int x=2; и с локальной переменной х return (x=x+5); и } и int functions(void) и { II определение функции functions static int x=3; и с локальной переменной х, с классом и памяти static } II /* Основная функция программы */ void main(void) { int у; // Объявление целой локальной переменной у /* функция functionl возвращает значение 7, т. к. начальное значение х = 2, а после return х = х + 5, х = 7 */ у = functionl(); /* функция functionl возвращает значение 12, т. к. значение х запомнилось благодаря static, и при вызове этой функции х = 7, а после return х-х+5, х-7+5-12*/ y=functionl(); /* функция function2 возвращает значение 7, т.к начальное значение х = 2, а после return х=х+ 5, х = 7 */ y=function2(); /* функция function2 снова возвращает значение 7, т. к. значение х не запомнилось и при вызове этой функции х снова инициализируется 2, а после return х=х+5, х=7*/ у = functions(); } В приведённом примере объявлены три разные переменные, имеющие оди- наковые имена х (разумеется, они могут иметь и различные имена). Каждая из этих переменных видна только в той функции, в которой она объявлена. Две
4.6. Переменные 181 переменные, объявленные как static (статические), имеют глобальное время жизни. Если статические переменные специально не инициализированы, то при за- пуске программы они автоматически устанавливаются в 0. Чтобы использовать переменные, которые глобально объявлены в других ис- ходных файлах проекта, переменные должны быть объявлены со спецификато- ром класса памяти extern (внешний). Цель такого объявления состоит в том, что- бы сделать определение переменной глобального уровня видимым внутри функ- ции. Пример: filel.c: /* Исходный файл filel.c */ /* Объявления переменной а */ extern int а; // Объявление переменной а /* теперь подключим файл, который содержит определение переменной а */ #include <file.h> // подключение файла file.h void main(void) { } filel.h: /* Заголовочный файл file.h */ int a=5; // определение переменной a Объявление переменной а как extern в приведённом примере делает её види- мой внутри функции main. Определение этой переменной находится в файле file.h на глобальном уровне. Во всех файлах, составляющих исходную программу, переменная, на которую делается ссылка с помощью спецификатора extern, может быть определена толь- ко один раз в одном из исходных или заголовочных файлов программы. Другие файлы могут содержать описания extern для доступа к ней. Описание extern может иметься и в том файле, где находится определение. Любая инициа- лизация такой переменной проводится ТОЛЬКО в определении. В определении должны указываться размеры массивов, а в описании extern это можно не делать. В объявлениях с классом памяти extern не допускается инициализация, так как эти объявления ссылаются на уже существующие и определённые ранее пере- менные. Объявление переменной со спецификатором extern информирует компилятор о том, что память для переменной выделять не требуется, так как это выполнено где-то в другом месте программы.
182 Глава 4. Основы языка Си В CodeVisionAVR объявление с классом памяти extern допускается только для глобальных переменных. Лучше избегать применения внешних переменных, так как они часто слу- жат источником труднообнаруживаемых ошибок. Для того чтобы полностью реализовать преимущество архитектуры и установ- ленных инструкций AVR, компилятор распределяет некоторые программные пе- ременные в регистрах микроконтроллера. Регистры с R2 по R14 можно определить для битовых переменных. Битовые переменные являются специальными глобальными переменными. Эти переменные объявляются, используя ключевое слово bit (бит). Синтаксис: bit <идентификатор>; Распределение памяти для битовых переменных осуществляется в порядке объявления, начиная с бита 0 регистра R2, затем бит 1 регистра R2 и так далее в порядке возрастания. Максимум можно объявить 104 битовых переменных. Пример: /* Битовые переменные */ bit bit_varl=l; // Объявление и инициализация битовой переменной bit_varl, // которая будет храниться в бите 0 регистра R2 bit bit_var2; // Объявление битовой переменной bit_var2, // которая будет храниться в бите 1 регистра R2 Объём (количество) битовых переменных в программе можно определить в настройках проекта (см. Закладка С Compiler (Компилятор Си)). Этот объём желательно делать как можно меньше, чтобы освободить регист- ры для распределения других глобальных переменных. Если битовые глобальные переменные специально не инициализированы, то при запуске программы они автоматически устанавливаются в 0. В выражениях битовые переменные автоматически переводятся в unsigned char (беззнаковые символьные). Если в настройках проекта (см. Закладка С Compiler (Компилятор Си)) выбра- на опция Automatic Register Allocation (Автоматическое распределение регистров) или используется директива компилятора #pragma regalloc+ (см. Директива ttpragma regalloc), то остальная часть регистров в диапазоне с R2 по R14, которые не использованы для битовых переменных, распределяются под char (символь- ные) и int (целые) глобальные переменные. Распределение реализуется в порядке объявления переменных, пока не будет распределён регистр R14. Для того чтобы не допустить помещение переменной в регистры и чтобы пре- дупредить компилятор, что она может подвергнуться изменению в процессе вы- числений, должен быть использован модификатор volatile (изменяющаяся). Пример: volatile int у; // Объявление целой переменной у, // которая не будет помещена в регистр
4.6. Переменные 183 Char (символьные) и int (целые) локальные переменные автоматически рас- пределяются, в порядке объявления, в регистры R16...R21. Если автоматическое распределение регистров запрещено, можно использо- вать ключевое слово register (регистр), чтобы определить глобальную перемен- ную, которую требуется распределить в регистры. Использование регистровой памяти обычно приводит к сокращению времени доступа к переменной. Пример: /* запретим автоматическое распределение регистров */ #pragma regalloc- /* распределим переменную d в регистр */ register int d; // Объявление целой переменной d, // которая будет помещена в регистр Все глобальные переменные, не распределённые в регистры, сохраняются в области Global Variables (Глобальных переменных) в SRAM. Все локальные пере- менные, не распределённые в регистры, сохраняются в динамически распределя- емом пространстве в области Data Stack (Стек данных) в SRAM (см. Организация Памяти SRAM). Глобальные переменные можно хранить в определённых ячейках SRAM, ко- торые можно задать в настройках проекта (см. Закладка С Compiler (Компилятор Си)), или с помощью оператора @. Пример: int х @0x90; // Объявление целой переменной х, // которая будет храниться в SRAM по адресу 9Oh 4.6.1. Массивы Переменные могут быть сгруппированы в массивы. Массивы — это группа элементов одинакового типа (double, float, int и т. п.). Из объявления массива ком- пилятор должен получить информацию о типе элементов массива и их количест- ве. Синтаксис объявления массивов: ^модификатор места хранения>] <тип> сидентификатор массива> [<константное_ выражение>]; Спецификатор типа задаёт тип элементов объявляемого массива. Элементами массива не могут быть функции и элементы типа void (неопределённые). Константное выражение в квадратных скобках задаёт количество элементов массива. Константное выражение при объявлении массива может быть опущено в следующих случаях: • при объявлении массив инициализируется. В этом случае компилятор сам автоматически определяет размер массива; • массив объявлен как формальный параметр функции; • массив объявлен как ссылка на массив, явно определённый в другом фай- ле.
184 Глава 4. Основы языка Си Компилятор CodeVisionAVR поддерживает многомерные массивы, которые могут иметь вплоть до 8 измерений. Многомерные массивы формализуются списком константных выражений, следующих за идентификатором массива, причём каждое константное выражение заключается в свои квадратные скобки. Каждое константное выражение в квадратных скобках определяет число элемен- тов по данному измерению массива, так что объявление двухмерного массива со- держит два константных выражения, трехмерного — три и т. д. В языке Си первый элемент массива ВСЕГДА имеет индекс, равный нулю. Если элементы массива глобальных переменных специально не инициализи- рованы, то при запуске программы они автоматически устанавливаются в 0. Если элементы массива локальных переменных специально не инициализированы, то при запуске программы они будут содержать всякий мусор. Пример: /* Примеры глобальных массивов */ int glob_arrayl [10]; // Объявление целочисленного глобального // массива glob_arrayl // При этом все элементы глобального // массива автоматически инициализируются //со значением 0 int glob_array2[3]={1, 2, 3}; // Объявление и инициализация // целочисленного глобального // массива glob_array2 int glob_array3[10]={4,28,16,35}; // Объявление и инициализация // целочисленного глобального // массива glob_array3 // При этом будут инициализированы // только первые 4 элемента массива // Остальные 6 элементов будут 0 int glob_array4[]={1,2,3,4}; // Объявление и инициализация // целочисленного глобального // массива glob_array4. // Компилятор автоматически определит // размер массива glob_array4[4] int glob_array5[3][2]={{5,0},{8,4},{3,0}}; // Объявление и инициализация И глобального целочисленного И многомерного И массива glob_array5 char glob_array6[]="Hello!"; // Объявление и инициализация символьного // глобального массива (строкового // литерала) glob_array6 /* Пример локальных массивов */
4.6. Переменные 185 void main(void) { int loc_arrayl[10]; // Объявление целочисленного локального // массива loc_arrayl int loc_array2[2][2]= {{4, 9}, {7,1}}; // Объявление и инициализация // целочисленного локального // многомерного // массива 1ос_аггау2 Следует учитывать, что в символьном литерале (строковой переменной, символьном массиве) находится на один элемент больше, так как послед- ний из элементов является управляющей последовательностью \0’. 4.6.2. Структуры Структуры — это составной объект, определённый пользователем, в который входят элементы любых типов, за исключением функций. В отличие от массива, который является однородным объектом, структура может быть неоднородной. Структуры оказываются полезными при организации сложных данных, осо- бенно в больших программах, поскольку во многих ситуациях они позволяют сгруппировать связанные данные таким образом, что с ними можно обращаться как с одним целым, а не как с отдельными объектами. Характерным примером структуры служит учётная карточка студента: фами- лия, имя, отчество (ф.и.о.), дата рождения, пол, факультет, группа, курс, специ- альность и т. д. В свою очередь некоторые из этих атрибутов сами могут оказаться структурами. Таковы, например, ф.и.о., дата рождения, имеющие несколько ком- понент. Структуры определяют, используя зарезервированное ключевое слово struct (структура). Синтаксис: ^модификатор места хранения>] struct [<метка-имя структуры>] { [<тип> <имя переменной> [, <имя переменной>, ...]]; [<тип>[<битовая область>]:<ширина>[,[<битовая область>]:<ширина>, } [<структурные переменные>]; В структуре обязательно должна быть указана хотя бы одна компонента. Пример: /* Глобальная структура, расположенная в SRAM */ /* Переменная str_ram определяется как структура, состоящая из шести компонент: целой переменной а, символьных переменных Ь и с, целочисленных массивов d[15] и е[20] и символьной переменной, расположенной по указателю next */
186 Глава 4. Основы языка Си struct structure_ram { int а; char c,b; int d[15],e[20] ; char *next; } str_ram; Пример: /* Глобальная структура, расположенная во FLASH, на что указывает ключевое слово flash перед struct */ /* Переменная str_flash определяется как структура, состоящая из двух компонент: символьной переменной а и целочисленного массива Ь[25] */ flash struct structure_flash { char a; int b[2 5]; } str_flash; Пример: /* Глобальная структура, расположенная в EEPROM, на что указывает ключевое слово eeprom перед struct */ /* Переменная str_eep определяется как структура, состоящая из четырех компонент: символьной переменной а, целой переменной b и двух целочисленных массивов с[30] и d[10] */ eeprom struct structure_eeprom { char a; int b; int c [30], d[10]; } str_eep; Пример: void main(void) { /* Локальная структура, т. к. расположена внутри функции */ /* Переменная str_loc определяется как структура, состоящая из двух компонент: длинной целой переменной а и целой переменной b */ struct structure_local { long а; int b; } str_loc;
4.6. Переменные 187 Пространство, занимаемое структурой в памяти, равняется сумме размеров всех элементов. Описание структуры, за которым не следует списка структурных переменных, не приводит к выделению какой-либо памяти; оно только определяет шаблон или форму структуры. Однако если такое описание снабжено меткой-именем, то эта метка-имя может быть использована позднее при определении фактических экземпляров структур. Пример: /* определение фактических экземпляров структур */ /* Описание структуры, за которым не следует списка структурных переменных. Это описание не приводит к выделению какой-либо памяти; оно только определяет шаблон или форму структуры, т. е. задаёт следующий тип данных - структура, состоящая из двух компонент: целой переменной а и символьной переменной с. Это описание снабжено меткой-именем structure_ram, которая далее использована при определении фактического экземпляра структуры */ struct structure_ram { int а; char с; }; /* Основная функция программы */ void main(void) { /* Локальная структура, т. к. расположена внутри функции */ /* Переменная str_ram определяется как структура, В CodeVisionAVR на структуры, хранящиеся во FLASH- и EEPROM-памяти, накладываются определённые ограничения: из-за того, что указатели по умолча- нию расположены в SRAM, они не могут использоваться в этих структурах. Поскольку в ассемблере Atmel AVRASM32 отдельные байты определяются с помощью директивы .DB, то во FLASH-памяти в действительности занимается 2 байта. Компилятор Си CodeVisionAVR заменит char (символьные) элементы структур, хранящиеся во FLASH, на int (целые). Также он расширит размер эле- ментов таких структур, представляющих символьные (char) массивы, до одинако- вых значений. Структуры могут быть сгруппированы в ОДНОМЕРНЫЕ массивы. Доступ к элементам структуры осуществляется с помощью выражения выбора элемента, которое имеет две формы: выражение.идентификатор выражение-идентификатор В первой форме выражение представляет величину типа struct, а идентифика- тор — это имя элемента структуры. Во второй форме выражение должно иметь
188 Глава 4. Основы языка Си значение адреса структуры, а идентификатор — имя выбираемого элемента структуры. Обе формы выражения выбора элемента дают одинаковый результат. Дейс- твительно, запись, включающая знак операции выбора (->), является сокращён- ной версией записи с точкой для случая, когда выражению, стоящему перед точ- кой, предшествует операция разадресации (*), примененная к указателю, т. е. за- пись выражение -> идентификатор эквивалентна записи (* выражение).идентификатор в случае, если выражение является указателем. Пример: /* Глобальный структурный массив, расположенный в EEPROM, на что указывает ключевое слово eeprom перед struct */ /* определение и инициализация */ eeprom struct eeprom_structure { int a; // Объявляем целую переменную а char b[10]; // Объявляем строковую переменную Ь char с; // Объявляем символьную переменную с } str_еер[2]={{12,"String",'х'}, {37,"word",'у'}}; // массив структурных // переменных str_eep /* Основная функция программы */ void main(void) { char kl,k2,k3,k4; // Объявляем символьные переменные kl,k2,k3,k4 int il,i2; // Объявляем целые переменные il,i2 /* определим указатель на структуру - ps */ struct eeprom_structure eeprom *ps; /* прямой доступ к элементам структуры */ il=str_eep[0]. а; kl=str_eep[0].b[1]; k2=str_eep[0].с; i2=str_eep[1].а; k3=str_eep[1].Ь[3] ; k4=str_eep[1].с; // Получим il=12 // Получим kl='t', второй символ строки // "String", т. к. первый символ b[0]='S' // Получим к2='х' // Получим i2=37 и Получим кЗ='d', четвёртый символ строки II "world",т. к. первый символ b[0]='w', и второй Ь[1]='о', третий Ь[2]=’г' II Получим к4='у'
4.6. Переменные 189 /* тот же доступ к элементам структуры, используя указатель */ ps=&str_eep; il=ps->a; kl=ps->b[l]; // Инициализируем указатель с адресом структуры // Получим il=12 // Получим kl='t', второй символ строки // "String", т. к. первый символ b[0]='S' k2=ps->c; + +ps; i2=ps->a; k3=ps->b[3]; // Получим к2 ='х' // инкриминируем (увеличиваем на 1) указатель // Получим i2=37 // Получим k3-'d', четвертый символ строки // "world",т. к. первый символ b[0]='w', // второй Ь[1]='о', третий Ь[2]='г' k4=ps->c; // Получим к4='у' } Поскольку некоторые устройства AVR имеют небольшой объём SRAM, то для того, чтобы держать размер Data Stack (Стек данных) небольшим, рекомендуется не передавать структуры как параметры функций, а использовать для этого указа- тели. типа structure_ram. Теперь под неё выделяется соответствующий объём памяти */ struct structure_ram str_ram; } Пример: /* Глобальная структура, расположенная в SRAM */ struct structure—ram { char a; char b[10]; int c,d,e; // Объявляем символьную переменную a // Объявляем строковую переменную b // Объявляем целые переменные c,d и е } str_ram={'w',"Hello",7,2}; // инициализируем структуру х, причём // переменная е осталась // неопределённой (для неё нет значения //в фигурных скобках) /* Определим функцию result */ /* Параметром функции является указатель point на структуру типа structure_ram */ /* Т. к. функция использует переменные, объявленные в структуре, тип функции будет такой же, как тип структуры structure_ram */ /* Отсюда получается такая запись, где сначала идет тип struct structure_ram, затем указатель (*) на функцию result, аргументом которой является выражение в скобках - указатель на структуру structure_ram */ struct structure_ram *result(struct structure_ram *point) /* Теперь определим саму функцию result */
190 Глава 4. Основы языка Си { /* элемент с=элемент а + элемент b */ point->e=point->c + point->d; // вычисляем значение е через указатели point->a='f'; // Для примера переопределим значение // символьной переменной а. Ее начальное // значение было 'w' /* возвращаем указатель структуры */ return point; } /* Основная функция программы */ void main(void) { int i; // Объявляем целую переменную i char x,y; // Объявляем символьные переменные х,у /* Вычисления через указатели */ /* str_ram->e=str_ram->c + str_ram->d */ /* i=str_ram->e */ /* Для этого вызываем функцию result, аргументом которой делаем адрес структуры str_ram (& - значит адрес). По указателю, который возвращает функция, определяем тип переменной е, значение которой вычисляется в функции result */ i=result(&str_ram)->е; // Получим i=7+2=9 /* Аналогично, воспользовавшись указателем, который возвращает функция result, можно определить и символьные переменные */ x=result(&str_ram)->а; // Получим х='f', т. к. значение // символьной переменной а переопределено //в функции result y=result(&str_ram)->b[3]; // Получим y='1', четвертый символ строки // "Hello", т. к. первый символ Ь[0]='Н', // второй Ь[1]='е', третий Ь[2]='1' } Для экономии места в памяти элементы структуры можно объявить как бито- вые поля (bit fields), имеющие ширину от 1 до 32 бит. Битовое поле обеспечивает доступ к отдельным битам памяти. Битовые поля распределяются в порядке объявления, начиная с наименее значимого бита. В общем случае тип структуры с битовыми полями задаётся в следующем ви- де:
4.6. Переменные 191 struct { unsigned идентификатор 1 : длина поля 1; unsigned идентификатор 2 : длина поля 2; } Длина поля задаётся целым выражением или константой. Эта константа опре- деляет количество битов, отведённое соответствующему полю. Поле нулевой длины обозначает выравнивание на границу следующего слова. Пример: /* Эта структура займёт 4 байта в SRAM как битовая область данных типа unsigned long (длинное без знака) */ struct structure_long unsigned long x:8; // Переменная x займёт биты 0...7 unsigned long у:10; // Переменная у займёт биты 8...17 unsigned long z:8; // Переменная z займёт биты 18...25 // Биты 26...31 не используются }; /* Эта структура займёт 2 байта в SRAM как битовая область данных типа unsigned int (целое без знака) */ struct structure_int { ); unsigned int х:б; unsigned int у:8; И II II Переменная Переменная Переменная х займёт биты 0...5 биты 6...13 биты 14,15 У Z займёт займёт unsigned int z :2; /* Эта структура займёт 1 байт в SRAM как битовая область , данных : типа unsigned char (символьное без знака) */ struct structure_char { unsigned char x: 2 ; и Переменная x займёт биты 0,1 unsigned char y:4; // Переменная у займёт биты 2...Б unsigned char z: 1; // Переменная z займёт бит б // Бит 7 не используется }; Ссылки на поле битов выполняются точно так же, как и на компоненты об- щих структур. Само же битовое поле рассматривается как целое число, макси- мальное значение которого определяется длиной поля.
192 Глава 4. Основы языка Си 4.6.3. Объединения (смеси) Объединения, как и структуры, — это составной объект, определённый пользо- вателем, в который входят элементы любых типов, за исключением функций, но в отличие от структур в каждый момент времени может использоваться (или, дру- гими словами, быть ответным) только один из элементов объединения. Объединение применяется для следующих целей: • инициализации используемого объекта памяти, если в каждый момент вре- мени только один объект из многих является активным; • интерпретации основного представления объекта одного типа, как если бы этому объекту был присвоен другой тип. Элементы объединения могут быть любыми из предусмотренных типов дан- ных, массивами этих типов данных или указателями на них. Память, которая соответствует переменной типа объединения, определяется величиной, необходимой для размещения наиболее длинного элемента объеди- нения. Когда используется элемент меньшей длины, то переменная типа объеди- нения может содержать неиспользуемую память. Все элементы объединения хра- нятся в одной и той же области памяти, начиная с одного адреса. Объединения определяют, используя зарезервированное ключевое слово union (объединение). Синтаксис: ^модификатор места хранения >] union [<метка-имя объединения:*] [<тип> <имя переменной:* [, <имя переменной:*, ..]]; [<тип> <битовая область:* :<ширина> [ ,<битовая область:*: <ширина>, . .]]; } [<переменные объединения:*] ; Объединения ВСЕГДА сохраняются в SRAM. Описание объединения, как и описание структуры (см. Структуры), за кото- рым не следует списка переменных объединения, не приводит к выделению ка- кой-либо памяти; оно только определяет шаблон или форму объединения. Однако если такое описание снабжено меткой-именем, то эта метка-имя может быть ис- пользована позднее при определении фактических экземпляров объединений. К элементам объединения можно осуществить доступ так же, как и к элемен- там структуры, с помощью выражения выбора элемента (см. Структуры). Только в первой форме выражение представляет величину типа union, а идентификатор — это имя элемента объединения. Ьо второй форме выражение должно иметь значе- ние адреса объединения, а идентификатор — имя выбираемого элемента объеди- нения. Пример: /* Объявление глобального объединения */ /* В данном случае в объединении задаётся только определение переменных без их инициализации */ union union_ram
4.6. Переменные 193 { unsigned int а; unsigned char b; // Объявляем целую переменную а // Объявляем символьную переменную b // Объявляем целую переменную с unsigned un_ram; int c; Основная функция программы */ void main(void) { unsigned char x; // Объявляем локальную символьную переменную х unsigned int у; // Объявляем локальную целую переменную у /* определим указатель point объединения union_ram с помощью знака (*) */ union union_ram *point; /* прямой доступ к элементам объединения */ /* Присвоим переменной а в объединении, например, значение ОЫ 0 0 0110 0 010 0 0110 = 0х8С4 6=3 5 910 */ un.ram. а=0Ы000110001000110 ; /* Объединение un_ram занимает в SRAM 16 бит (2 Б), т. к. такой размер имеет самый большой элемент объединения (int) (см. Табл. 5.4). Теперь в эту область памяти записано число 35910 */ х = un_ram.b; /* х примет значение 'F', т. к. переменная х имеет тип char, т. е. она имеет размер 8 бит (1 Б) (см. Табл. 5.4). Таким образом, значение х будет соответствовать числу, записанному в младших 8 битах области памяти, выделенной под объединение un_ram, т. е. х=0Ь1000110=0х46=70, а десятичный код 70 как раз и соответствует символу 'F' ASCII */ y=un_ram.с; /* у примет снова значение 35910, т. к. переменная с имеет тип int, т. е. она имеет размер 16 бит (2 Б), точно такой же, как у переменной а. Следовательно, значение у будет соответствовать числу, записанному во всех 16 битах области памяти, выделенной под объединение un_ram, т. е.у = 0Ы000110001000110 = 0х8С46 = 35910 */ } В данном примере все используемые переменные unsigned (беззнаковые), что- бы каждые бит имел смысл числа, а не знака.
194 Глава 4. Основы языка Си Поскольку некоторые устройства AVR имеют небольшой объём SRAM, то для того, чтобы держать размер Data Stack (Стек данных) небольшим, рекомендуется не передавать объединения, как параметры функций, а использовать для этого указатели. Главной особенностью объединения является то, что для каждого из объяв- ленных элементов выделяется одна и та же область памяти, т. е. они перекрыва- ются. Хотя доступ к этой области памяти возможен с использованием любого из элементов, но для этой цели он должен выбираться так, чтобы полученный ре- зультат не был бессмысленным. Приведённый выше пример абстрактный и особого смысла не имеет. Он лишь объясняет использование объединения. Пример: /* Объявление глобального объединения */ /* Это объединение позволяет получить отдельный доступ к младшему и старшему байту двухбайтного целого числа */ union union_int { unsigned int int_number; unsigned char byte_int[2]; } un_int; /* В этом объединении массив char byte_int, состоящий из двух элементов — byte_int[0] и byte_int[l], занимает в памяти ровно столько же места (2 х 1 Б), сколько и целая переменная number_int (2 Б), т. е. объединение un_int занимает в SRAM 2 Б */ /* Основная функция программы */ void main(void) { unsigned int num_int; // Объявляем локальную целую // переменную num_int unsigned char lsb_num_int; // Объявляем локальную символьную // переменную lsb_num_int (это будет // младший байт целого числа) unsigned char msb_num_int; // Объявляем локальную символьную // переменную msb_num_int (это будет // старший байт целого числа) /* Определим указатель point объединения union_int с помощью знака (*), т. е. зададим его тип. Его тип соответствует типу объединения */ union union_int *point; /* Зададим значение переменной, доступ к байтам которой требуется осуществить */
4.6. Переменные 195 num_int=0x3FA4; /* прямой доступ к элементам объединения */ un_int.int_number=num_int; // присваиваем значение переменной // num_int элементу int_number // объединения un_int /* Объединение un_int занимает в SRAM 16 бит (2 Б), т. к. такой размер имеют оба элемента объединения - переменная int и массив char (см. Табл. 5.4). Теперь в эту область памяти записано число 0x3FA4 */ /* Присваиваем переменной lsb_num_int значение элемента byte_int[0] объединения un_int. Значение этого элемента будет соответствовать числу, записанному в младших 8 битах (в младшем байте) области памяти, выделенной под объединение un_int, т. е. lsb_num_int=0xA4. Таким образом, переменная lsb_num_int принимает значение младшего байта переменной num_int */ lsb_num_int=un_int.byte_int[0]; /* Присваиваем переменной msb_num_int значение элемента byte_int[l] объединения un_int. Значение этого элемента будет соответствовать числу, записанному в старших 8 битах (в старшем байте) области памяти, выделенной под объединение un_int, т. е. msb_num_int=0x3F. Таким образом, переменная msb_num_int принимает значение старшего байта переменной num_int */ msb_num_int=un_int.byte_int[1]; /* Тот же доступ к элементам объединения, используя указатель */ point=&un_int; // Инициализируем указатель с // адресом объединения un_int point->int_number=num_int; // Получим il=12 lsb_num_int=point->byte_int[ 0 ] ; // Присваиваем переменной // lsb_num_int значение И младшего байта И переменной num_int msb_num_int=point->byte_int[1]; И Присваиваем переменной И msb_num_int значение И старшего байта И переменной num_int } Элементы объединения также можно объявить как битовые поля (bit fields), имеющие ширину от 1 до 32. Битовое поле обеспечивает доступ к отдельным би- там памяти.
196 Глава 4. Основы языка Си Битовые поля распределяются в порядке объявления, начиная с наименее значимого бита. В общем случае тип объединения с битовыми полями задаётся в следующем виде: union { unsigned идентификатор 1 : длина поля 1; unsigned идентификатор 2 : длина поля 2; } Длина поля задаётся целым выражением или константой. Эта константа опре- деляет количество битов, отведённое соответствующему полю. Пример: /* это объединение займёт 4 Б в SRAM как битовая область данных типа unsigned long (длинное без знака) */ union union_long { }; unsigned long х:8; unsigned long у:10; // Переменная х займёт биты 0...7 // Переменная у займёт биты 0...9 // Переменная z займёт биты 0...7 // Биты 10...31 не используются unsigned long z: 8; /* это объединение займёт 2 Б в SRAM как битовая область данных типа unsigned int (целое без знака) */ union union_int { unsigned int х:б; // Переменная x займёт биты 0...5 unsigned int y:8; // Переменная у займёт биты 0...7 unsigned int z:2; // Переменная z займёт биты 0,1 // Биты 8...15 не используются }; /* это объединение займёт 1 Б в SRAM как битовая область данных типа unsigned char (символьное без знака) */ union union_char { unsigned char х:2; // Переменная х займёт биты 0,1 unsigned char у: 4; и Переменная у займёт биты 0...3 unsigned char z:1; и Переменная z займёт бит 0 И Биты 4...7 не в используются }; Ссылки на поле битов выполняются точно так же, как и на компоненты об- щих объединений.
4.6. Переменные 197 4.6.4. Перечисления Переменная, которая может принимать значение из некоторого списка значе- ний, называется перечислением. Перечисления определяют, используя зарезервированное ключевое слово ешпп (перечисление). Синтаксис: ^модификатор места хранения >] enum [<метка-имя перечисления:*] { [<имя константы[[=константа-инициализатор], имя константы, ..]>] } [<переменные перечисления:*] ; Объявление перечисления задаёт тип переменной перечисления и определяет список элементов (именованных констант), называемый списком перечисления. Значением каждого имени списка является некоторое целое число. Переменная типа перечисления может принимать значения одной из именован- ных констант списка. Все элементы в списке ешпп должны быть уникальными. В случае отсутствия константного выражения первому элементу соответствует значе- ние 0, следующему — значение 1 и т. д. Имя элемента эквивалентно его значению. Элемент в списке, связанный с константным выражением, принимает значе- ние, задаваемое этим выражением. Следующему элементу в списке присваивается значение на 1 больше, если этот элемент не имеет своего константного выражения. Перечисления можно сохранить в SRAM или в EEPROM. Чтобы определить хранение в EEPROM, должно быть использовано ключевое слово eeprom. Пример: /★ Глобальное перечисление, расположенное в SRAM */ enum week { Saturday, // Этому элементу будет присвоено значение 0, // т. к. отсутствует константное выражение Sunday = 0, // Этому элементу будет присвоено значение 0 // благодаря константному выражению. Monday, // Этому элементу будет присвоено значение 1 Tuesday, // Этому - 2 Wednesday, // Этому — 3 Thursday, // Этому - 4 Friday // Этому - 5 } work_week; /★ Основная функция программы ★/ void main (void) { work_week = Wednesday; // Переменной work_week будет присвоено // значение 5 }
198 Глава 4. Основы языка Си Пример: /* Глобальное перечисление, расположенное в EEPROM */ eeprom епшп group { Petrov = 1, // Этому элементу будет присвоено значение 1 Ivanov, // Этому - 2 Sidorov, // Этому — 3 Belkin, // Этому - 4 Garin, // Этому - 5 Durov // Этому - б } student; /* Основная функция программы */ void main (void) { student = Sidorov; // Переменной student будет присвоено // значение 3 } Чтобы уменьшить размер и увеличить скорость выполнения скомпилированной программы, рекомендуется обрабатывать перечисления, как тип данных char (сим- вол), имеющий 8 битов. Для этого следует выбрать в настройках проекта опцию 8 bit enums (см. «Закладка С Compiler (Компилятор Си)») или использовать директиву #pragma 8bit_enums+ (см. «Директива ttpragma 8bit__enums»). 4.6.5. Инициализация данных Инициализация — это присвоение переменной начального значения. Выше инициализация уже несколько раз упоминалась. В этой главе будут просуммированы некоторые правила, относящиеся к инициализации. Начальное значение переменной можно присвоить при её объявлении, при- соединив инициатор к описателю. Инициатор начинается со знака «=» и имеет следующие форматы: Формат 1: = инициатор; Формат 2: = { список инициаторов }; Формат 1 используется при инициализации переменных основных типов (int, char и т. д.) и указателей, а формат 2 — при инициализации составных объектов (структур, объединений, массивов, перечислений). Пример: /* Инициализация данных */ char symbol= ' F'; // Переменная symbol инициализируется символом 'F'
4.6. Переменные 199 const long variable = (512 * 1024); Il Немодифицируемая переменная // (константа) variable // инициализируется константным // выражением, после чего она //не может быть изменена eeprom int arrayl[2][2] = {1,2,3,4}; // Инициализируется двухмерный // массив array1 целых величин, // расположенный в eeprom // Элементам массива присваиваются // значения из списка eeprom int arrayl[2][2] = {{1,2},{3,4}};// Та же самая инициализация int array2[2][2] = {{1,2},{3}}; // Если при инициализации указано меньше // значений для элементов массива, то // оставшиеся элементы инициализируются // 0, т. е. в данном случае элементы // первой строки получат значения 1 и 2, //а второй - 3 и 0 Для символьных массивов существует специальный способ инициализации: вместо фигурных скобок и запятых можно использовать строку (строковый литерал): char string[] = "Text"; Это сокращение более длинной, но эквивалентной записи: char string[] = { 'Т', 'е', 'х', 'f, '\0' }; Если размер массива любого типа опущен, то компилятор определяет его дли- ну, подсчитывая число начальных значений. В этом конкретном случае размер равен пяти (четыре символа плюс \0). В том случае, если задаётся размер массива, а строковый литерал длиннее, чем размер массива, то лишние символы отбрасываются. Если строка короче, чем размер массива, то оставшиеся элементы массива за- полняются нулями. 4.6.6. Файл распределения памяти глобальных переменных В процессе компиляции компилятор Си CodeVisionAVR генерирует Global Variables Memory Map File (Файл распределения памяти глобальных переменных), в котором определены адреса ячеек SRAM, распределение регистров и размер глобальных переменных, используемых программой. Этот файл имеет расшире- ние .тар и его можно просмотреть, используя команду меню File -> Open (Файл -> Открыть) или нажав кнопку Open (Открыть) на панели инструментов. Элементы структур и объединений указываются индивидуально вместе с их соответствующим адресом и размером. Этот файл полезен в процессе отладки программы при использовании отлад- чика AVR Studio.
200 Глава 4. Основы языка Си 4.7. Типы данных Важным отличием языка Си от других языков является отсутствие принципа умолчания, что приводит к необходимости объявления всех переменных, исполь- зуемых в программе явно вместе с указанием соответствующих им типов. Все типы данных, поддерживаемые компилятором Си CodeVisionAVR, их раз- меры и диапазоны их возможных значений приведены в Табл. 4.4. Таблица 4,4, Ъшы данных компилятора CodeVisionAVR Тип Размер (Биты) Диапазон bit (бит) 1 0, 1 char (символ) 8 -128...127 unsigned char (символ без знака) 8 0...255 signed char (символ со знаком) 8 -128...127 int(целое) 16 -32768...32767 short int (короткое целое) 16 -32768...32767 unsigned int (целое без знака) 16 0...65535 signed int (целое со знаком) 16 -32768...32767 long int (длинное целое) 32 -2147483648...2147483647 unsigned long int (длинное целое без знака) 32 0...4294967295 signed long int (длинное целое со знаком) 32 -2147483648...2147483647 float (с плавающей точкой) 32 ±1.175е-38...±3.402е38 double (двойное) 32 ±1.175е—38...±3.402е38 Тип данных bit (бит) поддерживается только для глобальных переменных. Если при конфигурировании проекта разрешена опция char is unsigned (сим- вол без знака) (см. Закладка С Compiler (Компилятор Си)) или использована ди- ректива ^pragma uchar+ (см. Директива ^pragma uchar), то по умолчанию тип char (символ) имеет диапазон 0...255, т. е. обрабатывается как тип unsigned char (сим- вол без знака). Все переменные должны быть описаны до их использования. Описание задаёт тип, за которым следует список одной или более переменной этого типа. Пример: int а, b; // Объявление переменных а и b как целых int temp; // Объявление переменной temp как целой char с; // Объявление переменной с как символьной ИЛИ /* Переменным в описаниях можно задавать начальные значения, объединяя, таким образом, описание и оператор присваивания */ int х=1; // Переменная х объявляется как целая, // и ей присваивается начальное значение 1
4.7. Типы данных 201 Следует отметить, что ключевые слова signed и unsigned необязательны. Они указывают, как интерпретируется самый старший бит объявляемой переменной. Старший бит для числа со знаком (signed) определяет знак числа. Если старший бит равен нулю, то число положительное, если же старший бит равен единице, то число отрицательное. Если указано ключевое слово unsigned, т. е. число беззнако- вое, то старший бит интерпретируется как часть числа. При отсутствии ключево- го слова unsigned переменная считается знаковой. Число +12 типа char (подразумевается signed char) будет храниться в памяти компьютера в виде: Бит 7 1 6 rz 4 1 3 2 1 1 1 0 Значение внпв Точно в таком же виде будет храниться и число +12 типа unsigned char. Если объявлено отрицательное число, то компилятор генерирует так называе- мый обратный код. Чтобы получить отрицательное число, надо поменять значе- ния всех битов на обратные, т. е. все 0 заменить на 1, а все 1 заменить на 0, и при- бавить к младшему биту 1. При этом в самом старшем бите должна быть 1, озна- чающая, что число отрицательное. Отрицательное число -12 типа char (подразумевается signed char) в двоичной записи в обратном коде будет иметь вид: Бит Значение Если это значение соответствует переменной типа unsigned char, то компиля- тор интерпретирует его как число 244. Для того чтобы понять различие в интерпретации компилятором чисел со знаком и чисел без знака, рассмотрим следующий пример. Пример: signed char xz а; // Объявление переменных х и а как символьных //со знаком unsigned char у, b; // Объявление переменных у и b как символьных // без знака void main(void) { x=0xFF; /* Поскольку переменная х имеет тип signed char (символьное со знаком), то компилятор интерпретирует ее значение (OxFF = 0b 1111 1111) как -1, т. к. 1 в самом старшем бите означает а остальные семь единиц в дополнительном коде означают 1 */ y=0xFF; /* т. к. переменная у имеет тип unsigned char (символьное без знака), то компилятор интерпретирует ее значение (0xFF=0b 1111 1111) как 255, т. к. все 1 являются частью числа */ а=х+у;
202 Глава 4. Основы языка Си Ь=х+у; /* В обоих случаях вычисляется сумма: ОЬ 1111 1111 + ОЬ 1111 1111 = Ob 1 1111 1110. Единица в самом старшем бите пропадает, т. к. под переменную типа char (как signed, так и unsigned) в памяти отводится 8 битов (1 Б). Таким образом, в обоих случаях результат сложения ОЬ 1111 1110, но компилятором он интерпретируется по-разному. Поскольку переменная а имеет тип signed char (символьное со знаком), то компилятор интерпретирует её значение как -2, т. к. 1 в самом старшем бите означает а остальные семь битов в дополнительном коде означают 2. Поскольку переменная Ь имеет тип unsigned char (символьное без знака), то компилятор интерпретирует ее значение как 254, т. к. все биты являются частью числа. Таким образом, после выполнения функции main: х=-1, у=255, а=-2, Ь=254 */ } В том случае, если в начале стоит ключевое слово signed или unsigned и далее следует идентификатор переменной, то она будет рассматриваться как перемен- ная типа int. Пример: unsigned int n; unsigned char b; // n - беззнаковая целая // b - беззнаковая символьная int c ; // подразумевается signed int с unsigned d; // подразумевается unsigned int d signed f; // подразумевается signed int f Следует также отметить, что модификатор типа char используется для пред- ставления символа (из массива представления символов) или для объявления строковых литералов. Значением объекта типа char является код размером 1 Б, соответствующий представляемому символу. Для представления символов рус- ского алфавита модификатор типа идентификатора данных имеет вид unsigned char, так как коды русских букв превышают величину 127. 4.8. Определение типов данных Кроме объявлений переменных различных типов, в компиляторе CodeVisionAVR имеется возможность объявить типы данных. Для этого следует использовать зарезервированное ключевое слово typedef. Синтаксис: typedef [кодификатор места хранения>] определение типа> <идентификатор>; Символу с именем <идентификатор> присваивается Определение типа>. Пример: /* определение типов данных */
4.9. Преобразования типов 203 /* Определим NEW - новое имя типа, представляющее указатель на функцию, возвращающую значения типа char */ typedef char (* NEW)( ); /* объявление переменных */ NEW alfa; /* Эта запись теперь означает, что alfa - это указатель на функцию, возвращающую значения типа char */ /* Эта запись эквивалентна объявлению */ char (* alfa)( ); Пример: /* определение типов данных */ /* Определим Array - новое имя типа, представляющее целочисленный массив из двадцати символов */ typedef int Array[20] /* объявление переменных */ Array data; /* Эта запись теперь означает, что data — это целочисленный массив из двадцати символов */ /* Эта запись эквивалентна объявлению */ int data[20]; 4.9. Преобразования типов Если в выражениях встречаются операнды различных типов, то компилятор преобразовывает их к общему типу в порядке приоритета: • если один из операндов типа float (плавающая точка), то другой операнд преобразовывается в этот же тип; • если один из операндов типа long int (длинное целое) или unsigned long (без- знаковое длинное), то другой операнд преобразовывается в этот же тип; • если один из операндов типа int (целое) или unsigned int (беззнаковое це- лое), то другой операнд преобразовывается в этот же тип. Типы char (символьный) и unsigned char (беззнаковый символьный) получают самый низкий приоритет. Таким образом, при вычислении выражений операнды преобразуются к типу того операнда, который имеет наибольший размер. В общем автоматически производятся только преобразования, имеющие смысл, такие как, например, преобразование целого (int) в число с плавающей
204 Глава 4. Основы языка Си точкой (float) в выражениях типа float + int. Выражения же, лишённые смысла, та- кие как использование переменной типа float в качестве индекса, запрещены. Пример: /* объявление переменных */ unsigned char ch; unsigned long long_int, result; int int_l, int_2; // объявление символьной переменной ch // объявление длинных целых // переменных long_int и result // объявление целых переменных // int_l и int 2 /* определение длинной целой переменной result */ result=ch*(long_int + int_l/int_2);// Присваиваем переменной result // значение выражения /* При выполнении оператора присваивания операнды ch, int_1 и int_2 будут преобразованы к типу unsigned long. Результат всего выражения будет иметь тип unsigned long */ Приоритеты преобразования типов можно изменить. В любом выражении может быть осуществлено («принуждено») явное преобразование типа с помо- щью конструкции, называемой cast (перевод). Синтаксис: (имя типа) выражение Пример: char ch_l, ch_2; int int_l, int_2, int_3; // int_2, int_3 // объявление символьных переменных ch_l, ch_2 // объявление целых переменных int_1, /* Инициализация переменных ch_l и int_l */ ch_l=l; int_l=0x3AF4; /* Вычисления с преобразованием типов */ int_2=ch_l+int_l; int_3=ch_l+(char)int_l; // Переменная ch_l будет преобразована в int. //В результате int_2=l+0x3AF4=0x3AF5 // Переменная int_l будет преобразована в char // благодаря переводу (char). При таком // преобразовании будет отброшен старший байт // числа int_l, т. к. int занимает 2 байта, //a char - 1. //В результате int_2=l+0xF4=0xF5
4.9. Преобразования типов 205 ch_2=ch_l+int_l; // Переменная ch_l будет преобразована в int. // В результате получим 1+0x3AF4=0x3AF5. // Так как ch_2 имеет тип char, результат будет // преобразован в char, т. е. будет отброшен // старший байт. //В результате ch_2=0xF5 При преобразовании типов с использованием перевода следует быть внима- тельным, чтобы результат не потерял смысла, т. к. при таких преобразованиях от- брасываются старшие байты чисел, которые могут быть значимыми. Чтобы уменьшить размер и увеличить скорость выполнения скомпилированной программы для 8-битных чипов AVR, рекомендуется НЕ ВЫБИРАТЬ в конфигура- ции проекта опцию Promote char to int (Переводить символьное в целое) (см. Закладка С Compiler (Компилятор Си)) и в программе не использовать директи- ву #pragma promotechar+ (см. Директива ^pragmapromotechar). При этом типы опе- рандов char (символ) или соответственно unsigned char (символ без знака) автомати- чески НЕ ПЕРЕВОДЯТСЯ в int (целое) или соответственно в unsigned int (целое без знака), как в компиляторах, предназначенных для 16- или 32-битных CPU. Преобразование типов также может потребоваться, чтобы предотвратить пе- реполнение при 8-битном сложении или умножении. Компилятор в этих ситуа- циях выдаёт предупреждение. Пример: /* Преобразование типов данных */ void main(void) { /* Объявляем и сразу инициализируем символьные переменные х и у */ unsigned char х = 200; unsigned char у = 100; /* Объявляем целую переменную z */ unsigned int z; /* Производим вычисления */ z = х+у; /* В этом выражении будет получен неправильный результат (z = 44), поскольку сложение сделано с 8-битными числами и результат будет 8-битным, что в данном случае приведёт к переполнению. Результат преобразуется в unsigned int (беззнаковое целое) только после сложения */ z=(unsigned int)х+у; /* В этом выражении будет получен правильный результат (z=300), поскольку здесь перевод заставляет делать сложение
206 Глава 4. Основы языка Си 16-битных чисел. Результат будет 16-битным, без переполнения */ } Компилятор CodeVisionAVR ведёт себя иначе для следующих операторов-вы- ражений: += *_ /= %= &= Для этих операторов-выражений тип результата должен быть записан после крайнего левого операнда (который должен быть переменной). Таким образом, компилятор всегда будет преобразовывать операнд, находящийся справа, в тип крайнего левого операнда. 4.10. Операнды и операции Комбинация знаков операций и операндов, результатом которой является опре- делённое значение, называется выражением. Знаки операций определяют действия, которые должны быть выполнены над операндами. Каждый операнд в выражении может быть выражением. Значение выражения зависит от расположения знаков опе- раций и круглых скобок в выражении, а также от приоритета выполнения операций. Присваивание также является выражением, и значением такого выражения является величина, которая присваивается. Операнд — это константа, литерал, идентификатор, вызов функции, индекс- ное выражение, выражение выбора элемента или более сложное выражение, сформированное комбинацией операндов, знаков операций и круглых скобок. Любой операнд, который имеет константное значение, называется константным выражением. Каждый операнд имеет тип. Выражения со знаками операций могут участвовать в выражениях как опе- ранды. Выражения со знаками операций могут быть унарными (с одним операн- дом), бинарными (с двумя операндами) и тернарными (с тремя операндами). 4.10.1. Унарные операции Унарное выражение состоит из операнда и предшествующего ему знака унар- ной операции и имеет следующий формат: знак-унарной-операции операнд В Табл. 4.5 перечислены унарные операции компилятора CodeVisionAVR и приведены их знаки.
4.10. Операнды и операции 207 Таблица 4.5. Унарные операции компилятора CodeVisionAVR Знак операции Операция $<?>$ арифметическое отрицание (отрицание и дополнение) побитовое логическое отрицание (дополнение) ! логическое отрицание ♦ разадресация (косвенная адресация) & вычисление адреса ++ инкремент — декремент sizeof размер Унарные операции выполняются справа налево. Операция арифметического отрицания ($<?>$) вырабатывает отрицание своего операнда. Операнд должен быть целым (int) числом или числом с плаваю- щей точкой (float). Пример: /* Унарная операция (-) */ int х = 12; // Объявляем и инициализируем целую переменную х х = -х; // переменной х присваивается её отрицание, т. е // х принимает значение -12 Операция двоичного дополнения (~) вырабатывает двоичное дополнение своего операнда. После этой операции все 1 в двоичном коде значения операнда меняются на 0, а все 0 — на 1. Пример: /* Унарная операция (~) */ unsigned char а=0Ь11001001;// Объявляем и инициализируем беззнаковую unsigned char b; // символьную переменную а // Объявляем беззнаковую символьную // переменную b b=~a; //С переменной а делаем операцию // двоичного дополнения и полученное // значение присваиваем // переменной Ь. В результате // b = ObOOllOllO = ОхЗб = 54 Операция логического отрицания НЕ (!) вырабатывает значение 0, если опе- ранд есть истина (не ноль), и значение 1, если операнд равен нулю (0). Пример: /* Унарная операция (!) */ int х; // Объявляем целую переменную х int у=0; // Объявляем и инициализируем целую переменную у
208 Глава 4. Основы языка Си int z=0x4f; // Объявляем и инициализируем целую переменную z х=! у ; И С переменной у делаем операцию логического отрицания И и полученное значение присваиваем переменной х. И В результате переменная х получит значение, равное 1, И так как переменная у имела значение, равное 0 (ложно). х=! z; И С переменной z делаем операцию логического отрицания И и полученное значение присваиваем переменной х. И В результате переменная х получит значение, равное 0, И так как переменная z имела значение, II не равное 0 (истина). Операция разадресации (*) осуществляет косвенный доступ к адресуемой ве- личине через указатель. Операнд должен быть указателем. Результатом операции является величина, на которую указывает операнд. Результат не определён, если указатель содержит недопустимый адрес. Операция вычисления адреса (&) даёт адрес своего операнда. Операндом мо- жет быть любое именуемое выражение. Результатом операции вычисления адреса является указатель на операнд. Пример: /* Унарные операции (*) и (&) */ int х; int у=5; // Объявляем целую переменную x // Объявляем и инициализируем целую переменную у int *adress; // Объявляем переменную adress как указатель, на // что указывает знак * adress=&x; // переменной adress присваивается адрес (&) переменной х *adress=y; // переменной, находящейся по адресу, содержащемуся в // переменной adress, присваивается значение переменной у, // т. е. 5 , что эквивалентно записи х=у;т. е. х=5 adress=&y; // переменной adress присваивается адрес (&) переменной у x=*adress; // переменной х присваивается значение, находящееся по // адресу, содержащемуся в переменной adress, т. е. // значение переменной у, что эквивалентно записи х = у; // т. е. х = 5. Операции инкремента и декремента увеличивают или уменьшают значение операнда на единицу и могут быть записаны как справа, так и слева от операнда. Если знак операции записан перед операндом (префиксная форма) (например, ++х), то изменение операнда происходит до его использования в выражении. Ес- ли знак операции записан после операнда (постфиксная форма) (например, х++)> то операнд вначале используется в выражении, а затем происходит его изменение. Пример: /* Унарные операции (--) и (++) */ int у; int х = 5; --х; + +х; у = 5 + х++; // Объявляем целую переменную у // Объявляем и инициализируем целую переменную х // Переменная х будет уменьшена на 1, х = 4 // Переменная х будет увеличена на 1, х = 5 // Сначала берется текущее значение х (х = 5), к нему прибавляется
4.10. Операнды и операции 209 // значение 5, полученный результат присваивается переменной у. // После этого значение х увеличивается на 1. Таким образом, // после выполнения этой строки у = 10, х = б. С помощью операции sizeof можно определить размер памяти в байтах, которая соответствует идентификатору или типу Операция sizeof имеет следующий формат: s i z ео f(выражение) В качестве выражения может быть использован любой идентификатор либо имя типа, заключённое в скобки. Пример: /* Унарная операция (sizeof) */ double х; int у; y=sizeof(х); // Объявляем переменную х двойной точности // Объявляем целую переменную у //у=4, т. к. х имеет тип double и занимает //в памяти 4 Б // у=1, т. к. тип char занимает в памяти 1 Б. y=sizeof(char); 4.10.2. Бинарные операции Бинарное выражение состоит из двух операндов, разделённых знаком бинар- ной операции, и имеет следующий формат: операнд! знак-бинарной-операции операнд2 В отличие от унарных бинарные операции, список которых приведён в Табл. 4.6, выполняются слева направо. Таблица 4,6. Бинарные операции компилятора CodeVisionAVR Знак операции Операция * Умножение / Деление % Остаток от деления + Сложение - Вычитание « Сдвиг влево » Сдвиг вправо > Больше < Меньше <= Меньше или равно >= Больше или равно == Равно ! = Не равно
210 Глава 4. Основы языка Си (продолжение) Знак операции Операция & Побитное И | Побитное ИЛИ А Побитное исключающее ИЛИ АЛ Логическое И II Логическое ИЛИ Последовательное вычисление = Присваивание *= Умножение с присваиванием /= Деление с присваиванием %= Остаток от деления с присваиванием -= Вычитание с присваиванием += Сложение с присваиванием <<= Сдвиг влево с присваиванием »= Сдвиг вправо с присваиванием &= Побитное И с присваиванием 1= Побитное ИЛИ с присваиванием А= Побитное исключающее ИЛИ с присваиванием Операция умножения (*) выполняет умножение операндов. Операция деления (/) выполняет деление первого операнда на второй. Если две целые величины не делятся нацело, то результат округляется в сторону нуля. Операция остаток от деления (%) показывает остаток от деления первого опе- ранда на второй. Операндами этой операции должны быть целые числа. Пример: /* Бинарные операции (*), (/) и (%) */ int а=37, Ь=10; И Объявляем и инициализируем целые переменные а и Ь int х, у, z; // Объявляем целые переменные х, у и z x=a*b; // х=370 у=а/Ь; // у=3 z=a%b; // z=7 Результатом выполнения операции сложения (+) является сумма двух опе- рандов. Операция вычитания (-) вычитает второй операнд из первого. Пример: /* Бинарные операции (+) и (-) */ int а=37, Ь=10; int х, у; // Объявляем и инициализируем целые переменные а и Ь // Объявляем целые переменные х и у
4.10. Операнды и операции 211 х=а+Ь; у=а-Ь; И х=47 И у=27 Операции сдвига осуществляют смещение значения первого операнда влево (<<) или вправо (») на количество битов, задаваемое вторым операндом. Оба опе- ранда должны быть целыми величинами. Выполняются обычные арифметические преобразования. При сдвиге влево правые освобождающиеся биты устанавливают- ся в ноль. При сдвиге вправо метод заполнения освобождающихся левых битов за- висит от типа первого операнда. Если тип unsigned, то свободные левые биты уста- навливаются в ноль. В противном случае они заполняются копией знакового бита. Результат операции сдвига не определён, если второй операнд отрицательный. Пример: /* Бинарные операции (<<) и (>>) */ int а=0Ы01001; int х, у; х=а«2 ; у=а»1; // Объявляем и инициализируем целую переменную а // Объявляем целые переменные х и у // сдвигаем значение а на 2 бита влево и полученное // значение присваиваем переменной х; х=0Ы0100100 // сдвигаем значение а на 1 бит вправо и полученное // значение присваиваем переменной у; у=0Ы0100 // Значение переменной а не изменяется! а=0Ь101001 Обратите внимание, что сдвиг влево соответствует умножению первого операнда на степень числа 2, равную второму операнду, а сдвиг вправо со- ответствует делению первого операнда на 2 в степени, равной второму операнду. Операции отношения (<, >, <=, >=, ==, !=) служат для сравнения значений двух операндов. Пример: /* Бинарная операция (!=) */ if (а’=Ь) // Если значение а не равно значению Ь, else [группа операторов!] [группа операторов2] // то выполняется [группа операторов!] // Иначе, если значение а равно значению Ь, // выполняется [группа операторов2] Операция побитного логического И (&) сравнивает каждый бит первого опе- ранда с соответствующим битом второго операнда. Если оба сравниваемых бита единицы, то соответствующий бит результата устанавливается в 1, в противном случае — в 0. Операция побитного логического ИЛИ (|) сравнивает каждый бит первого операнда с соответствующим битом второго операнда. Если любой (или оба) из сравниваемых битов равен 1, то соответствующий бит результата устанавливается в 1, в противном случае результирующий бит равен 0. Операция побитного исключающего ИЛИ (А) сравнивает каждый бит первого операнда с соответствующим битом второго операнда. Если один из сравнивае- мых битов равен 0, а второй бит равен 1, то соответствующий бит результата уста-
212 Глава 4. Основы языка Си навливается в 1, в противном случае, т. е. когда оба бита равны 1 или 0, соответ- ствующий бит результата устанавливается в 0. Пример: /* Бинарные операции (&, I и А) */ int а=0хА5С8; // Объявляем и инициализируем целую переменную а со И значением а = ОЬ1О1ОО1О111ОО1ООО...1О1О 0101 1100 1000 int b=0x0B8D; И Объявляем и инициализируем целую переменную Ь со И значением Ь = 0Ы01110001101...0000 1011 1000 1101 int с; И Объявляем целую переменную с с = а&Ь; И с = 0x0188=Obi10001000 - 0000 0001 1000 1000 с = alb; И с = 0xAFCD=0bl010111111001101 - 1010 1111 1100 1101 с - аАЬ; И с = 0хАЕ45=0Ь1010111001000101 - 1010 1110 0100 0101 Операция логического И (&&) вырабатывает значение 1, если оба операнда име- ют ненулевые значения. Если один из операндов равен 0, то результат также равен 0. Операция логического ИЛИ (||) выполняет над операндами операцию ИЛИ. Она вырабатывает значение 0, если оба операнда имеют значение 0. Если ка- кой-либо из операндов имеет ненулевое значение, то результат операции равен 1. Пример: /* Бинарные операции (&& и II) */ int а = 0хА5С8; int b = 0x0B8D; int с = 0, d=0; int result; result = a&&b; result = a&&c; result = c&&d; result = a I Ib; result = a I Ic; result = с I Id; // Объявляем и инициализируем целую переменную а // Объявляем и инициализируем целую переменную b // Объявляем и инициализируем целые переменные end // Объявляем целую переменную result // result = 1 // result = 0 // result = 0 // result = 1 // result = 1 // result = 0 Операция последовательного вычисления (,) используется для вычисления двух и более выражений там, где по синтаксису допустимо только одно выраже- ние. Эта операция вычисляет два операнда слева направо. Пример: /* Бинарная операция (,) */ int а = 0хА5С8, b = 0x0B8D; // Последовательно объявляем и инициализируем // целые переменные а и Ь Запятая может использоваться также как разделитель, поэтому необ- ходимо по контексту различать запятую, используемую в качестве разде- лителя или знака операции. Операция простого присваивания (=) используется для замены значения ле- вого операнда значением правого операнда. В языке Си присваивание также яв-
4.10. Операнды и операции 213 ляется выражением, и значением такого выражения является величина, которая присваивается. Пример: /* Бинарная операция (=) */ int а; int Ъ; int * adress; а=5; b=3; Ь=а; adress = &а; // Объявляем целую переменную а // Объявляем целую переменную b // Объявляем переменную adress как указатель, на // что указывает знак * // Присваиваем переменной а значение 5 // Присваиваем переменной Ь значение 3 // Присваиваем переменной Ь значение переменной а, Ь = 5 // Присваиваем переменной adress адрес (&) переменной а Кроме простого присваивания, имеется целая группа операций присваивания (*=, /=, %=, -=, +=, <<=, »=, &=, |= и А=), которые объединяют простое при- сваивание с одной из бинарных операций. Такие операции называются составны- ми операциями присваивания и имеют вид (операнд!) (бинарная операция) = (операнд2) Составное присваивание по результату эквивалентно следующему простому присваиванию: (операнд!) = (операнд!) (бинарная операция) (операнд2) Пример: /* Бинарные операции (*=, /=, +=) */ int а - 5; // Объявляем и инициализируем целую переменную а int Ь = 4; // Объявляем и инициализируем целую переменную Ь а+ = 3; // эквивалентно а = а+3, в результате а = 8 а / = Ь ; // эквивалентно а = а/b, в результате а = 8/4 = = 2 Ь* = а; // эквивалентно Ь = Ь*а, в результате Ь = 4*2 = = 8 Операции &=, |= и А= часто называют накладыванием маски, а число, стоящее справа от знака такой операции, называют маской. При накладывании маски из- меняются значения лишь определённых битов операнда, стоящего слева. Этими операциями пользуются в тех случаях, если в переменной требуется из- менить один или несколько битов, а остальные оставить без изменений. Пример: /* Бинарные операции &=, I= и А= как операции накладывания маски */ unsigned char unsigned char unsigned char x=0b00111100; y-0b00111100; z=0b00111100; // Объявляем и инициализируем символьные // (байтовые) переменные х,у и z // /* Наложим на эти числа маску ОЬООООШ! */ х&=0Ь00001111;
214 Глава 4. Основы языка Си /★ При накладывании маски операцией &= в тех битах, где в маске стоит значение 0, биты исходного числа принимают значение 0, независимо от первоначального значения. В тех битах, где в маске стоит значение 1, биты исходного числа не изменяются Таким образом, в результате х=0Ь00001100 */ у I=0600001111; /* При накладывании маски операцией 1= в тех битах, где в маске стоит значение 1, биты исходного числа принимают значение 1, независимо от первоначального значения. В тех битах, где в маске стоит значение 0, биты исходного числа не изменяются Таким образом, в результате у=0Ь00111111 */ zА=0Ь00001111; /* При накладывании маски операцией А= в тех битах, где в маске стоит значение 1, биты исходного числа принимают значение, противоположное первоначальному, т. е. если был 0, то станет 1, если была 1, то станет 0 В тех битах, где в маске стоит значение 0, биты исходного числа не изменяются Таким образом, в результате z=0b00110011 */ При записи выражений следует помнить, что символы (*), (&), (!) могут обозначать унарную или бинарную операцию. 4.10.3. Тернарные операции В языке Си имеется всего одна тернарная операция — условная операция. Тернарное выражение состоит из трёх операндов, разделённых знаками тернар- ной операции (?) и (:), и имеет формат операнд! ? операнд2 : операндЗ Если операнд! не равен 0, то вычисляется операнд!, и его значение является результатом операции. Если операнд! равен 0, то вычисляется операндЗ, и его значение является результатом операции. Следует отметить, что вычисляется ли- бо операнд!, либо операндЗ, но не оба. Фактически условная операция представляет собой сокращённую форму опера- тора if-else (см. Оператор if — else). Таким образом, вместо if(x >у) тах=х; else тах=у; достаточно написать: тах=(х>у) ?х: у Скобки вокруг операнда! ставить необяза- тельно, так как приоритет операции ? очень низкий, ниже он только у операции присваивания (см. Приоритеты операций и порядок вычислений). Пример: /* Тернарная операция */ /* Переменной maximum присваивается максимальное из значений переменных а и Ь */ maximum = (a<=b) ? Ь : а;
4.10. Операнды и операции 215 4.10.4. Приоритеты операций и порядок вычислений В языке Си операции с высшими приоритетами вычисляются первыми. На- ивысшим является приоритет, равный 1. Приоритеты и порядок операций приве- дены в Табл. 4.7. Таблица 4.7. Приоритеты операций и порядок вычислений Приоритет Типы операции Знак операции Порядок выполнения 1 Выражение 0 [] Справа налево 2 Унарные * & sizeof type (приведение типов) Слева направо 3 Мультипликативные * / % Слева направо 4 Аддитивные + Слева направо 5 Сдвиг « Слева направо 6 Отношение < <= >= Слева направо 7 Отношение (равенство) != Слева направо 8 Побитное И & Слева направо 9 Побитное исключающее ИЛИ А Слева направо 10 Побитное ИЛИ | Слева направо 11 Логическое И ЛА Слева направо 12 Логическое ИЛИ II Слева направо 13 Условная Слева направо 14 Простое и составное присваивание *= /= %= += &= >>= А_ Справа налево 15 Последовательное вычисление Слева направо
216 Глава 4. Основы языка Си Пример: /* Приоритеты операций и порядок вычислений */ unsigned char х= Obi1110000; // Объявляем и инициализируем // беззнаковый байт х unsigned char у = ОЬООШЮО; // Объявляем беззнаковый байт у у&=~хIОхАА; /* Первой выполняется операция побитового логического отрицания (~) как имеющая самый высокий приоритет. После её выполнения все 1 в переменной х заменятся на 0, а 0 - на 1 (см. Унарные операции). Затем выполняется операция побитного ИЛИ (I) полученного значения (ОЬООООИИ) с числом 0хАА=0Ь10101010. При этом каждый бит первого операнда сравнивается с соответствующим битом второго операнда. Если любой (или оба) из сравниваемых битов равен 1, то соответствующий бит результата устанавливается в 1, в противном случае - в 0 (см. Бинарные операции). Последней выполняется операция побитного ИЛИ с присваиванием (&=) значения у и результата предыдущих операций (OblOlOllll). При этом каждый бит первого операнда сравнивается с соответствующим битом второго операнда. Если оба сравниваемых бита 1, то срответствующий бит результата устанавливается в 1, в противном случае - в 0 (см. Бинарные операции). Полученное значение (ОЬООЮНОО) присваивается переменной у. Результат: у=0Ь00101100 */ 4.11. Операторы Порядок выполнения программы задают управляющие операторы. В приве- дённых ранее примерах уже встречались наиболее употребительные управляющие конструкции компилятора Си CodeVisionAVR. Здесь будут описаны остальные операторы управления и уточнены действия операторов, обсуждавшихся ранее. Все операторы языка Си, кроме составных операторов, заканчиваются точкой с запятой (;), но в CodeVisionAVR допускается и после составного оператора ста- вить точку с запятой. Любой оператор в программе может быть обозначен меткой, состоящей из имени и следующего за ним двоеточия. Все операторы языка Си можно условно разделить на несколько категорий (см. Табл. 4.8). Таблица 4,8. Операторы языка Си Категория Оператор Условные операторы if — else switch Операторы цикла for while do while
4.11. Операторы 217 (продолжение) Категория Оператор Операторы перехода break continue return goto Другие операторы Оператор-выражение Пустой оператор Составной оператор 4.11.1. Оператор if-else Оператор if-else используется при необходимости сделать выбор. Синтаксис: if (выражение) [группа операторов!] else [группа операторов2] Часть с else является необязательной. Сначала вычисляется выражение] если оно истинно (т. е. значение выражения отлично от нуля), то выполняется группа операторов 1. Если оно ложно (значение выражения равно нулю) и если есть часть с else, то группа операторов 1 пропускается и выполняется группа операторов2. Ес- ли часть с else отсутствует, то выполняется следующий за if оператор программы. Пример: /* Оператор if-else */ /* Переменной с будет присвоено значение переменной, наибольшей из а и b */ if (а > Ь) // Если а > b с = а; // то с = а, else // иначе, с - Ь; // с = b 4.11.2. Оператор switch Оператор switch предназначен для организации выбора из множества различ- ных вариантов, который заключается в проверке совпадения значения данного выражения с одной из заданных констант и соответствующего ветвления. Синтаксис: switch (выражение) {
218 Глава 4. Основы языка Си [объявление] [case константное-выражение!]: [группа операторов!] [case константное-выражение2]: [группа операторов2] [default: [группа операторовп]] } Выражение, следующее за ключевым словом switch в круглых скобках, может быть любым выражением, допустимым в языке Си, значение которого должно быть целым. Значение этого выражения является ключевым для выбора из не- скольких вариантов. Тело оператора switch состоит из нескольких операторов, по- меченных ключевым словом case с последующим константным выражением. Каждый вариант должен быть помечен либо целым числом, либо символьной константой, либо константным выражением. Если значение константного выра- жения, стоящего после case, совпадает со значением целого выражения, следую- щего за ключевым словом switch, то выполнение начинается с этого варианта. Все константные выражения в операторе switch должны быть уникальны. Кроме опе- раторов, помеченных ключевым словом case, может быть (обязательно один) фрагмент, но помеченный ключевым словом default. Если ни один из вариантов не подходит, то выполняется оператор, стоящий после default. Префикс default является необязательным; если его нет и ни один из случаев не подходит, то управление передаётся на следующий после switch опера- тор. Варианты (case) и выбор по умолчанию (default) могут располагаться в лю- бом порядке. После выполнения операторов, соответствующих выбранному варианту, бу- дут выполняться операторы, соответствующие следующему варианту. Для выхода из оператора switch используется оператор break. Пример: /* Оператор switch */ int х=2; // Объявляем и инициализируем целую переменную у switch (х) И Оператор switch { case 0: x=x-2; II Тело оператора switch case 3: x=x-2; II case 2: x-x*5; И case 5: x=x/2; break; // Строка с оператором break case 4: x=x+l; // default • / // } [группа операторов] /* Выполнение оператора switch начинается с оператора, помеченного case 2, т. к. х=2. Таким образом, переменная х получает значение х=2*5=10. Затем выполняется оператор, помеченный ключевым словом case 5 (по порядку), х получает значение х=10/2=5. Далее по оператору break происходит выход из оператора switch на [группу операторов]. Если бы break не было, то еще выполнялись бы операторы, помеченные case 4 и default, а уже затем [группа операторов] */
4.11. Операторы 219 4.11.3. Оператор for Оператор for — это наиболее общий способ организации цикла. Он имеет сле- дующий формат: Синтаксис: for (выражение! ; выражение2 ; выражениеЗ) тело Выражение! обычно используется для установления начального значения па- раметра цикла. Выражение2 — это выражение, определяющее условие, при кото- ром тело цикла будет выполняться. ВыражениеЗ определяет закон изменения па- раметра цикла после каждого выполнения тела цикла. При выполнении оператора for сначала вычисляется выражение!, затем выражение2. Если значение выражения2 отлично от нуля (истина), выполняет- ся тело цикла, вычисляется выражениеЗ, снова вычисляется выражение2, и если оно по-прежнему отлично от нуля, то цикл повторяется. Если выражение2 рав- но нулю (ложь), то управление передаётся на оператор, следующий за операто- ром for. Существенно то, что проверка условия всегда выполняется в начале цикла. Это значит, что тело цикла может ни разу не выполниться, если условие выполне- ния сразу будет ложным. Пример: /* Оператор for */ /* В этом примере вычисляется сумма чисел от 1 до 10 */ void main(void) { int x; // Объявляем целую переменную х int у=0; // Объявляем и инициализируем целую переменную у int z=10; // Объявляем и инициализируем целую переменную z for (х=1; x<=z; х=х+1) // Оператор цикла { у=у+х; // Тело цикла } } /* Тело цикла (у=у+1) будет выполняться, начиная со значения х=1. После каждого выполнения цикла х будет увеличиваться на 1 и тело цикла будет выполняться с новым значением х. Цикл будет выполняться до тех пор, пока х не превысит значение z, т. е. 10. Обратите внимание, что после выполнения цикла for значение х будет равно 11. Значение у будет равно 55 */ Другим вариантом использования оператора for является бесконечный цикл. Для организации такого цикла можно использовать пустое условное выражение, а для выхода из цикла обычно используют дополнительное условие и оператор break. Если отсутствует проверка, т. е. выражение2, то считается, что оно всегда истинно.
220 Глава 4. Основы языка Си Пример: /* Бесконечный цикл с оператором for */ for (;;) { ... break; } 4.11.4. Оператор while Оператор цикла while называется циклом с предусловием. Синтаксис: while (выражение) тело ; В качестве выражения допускается использовать любое выражение языка Си, а в качестве тела — любой оператор, в том числе пустой или составной. Сначала вычисляется выражением если оно ложно (т. е. значение выражения рав- но нулю), то выполнение оператора while заканчивается и выполняется следующий по порядку оператор. Если выражение истинно (значение выражения отлично от нуля), то выполняется тело оператора while и процесс повторяется сначала. Оператор цикла вида for (выражение!; выражение2; выражениеЗ) тело ; может быть заменён оператором while следующим образом: выражение!; while (выражение2) { тело выражениеЗ; } Как и при выполнении оператора for, в операторе while вначале происходит проверка условия. Поэтому оператор while удобно использовать в ситуациях, ког- да тело оператора не всегда нужно выполнять. Пример: /* Оператор while */ /* В этом примере вычисляется факториал числа 5 */ void main(void) int x=l; // Объявляем и инициализируем целую переменную х int y=l; // Объявляем и инициализируем целую переменную у int z=5; // Объявляем и инициализируем целую переменную z while (x<=z) // Оператор цикла {
4.11. Операторы 221 /* Тело цикла while */ у=у*х; // вычисляем значение у х=++х; // инкрементируем (увеличиваем на 1) } // значение х /* Тело цикла (у=у*х, х=++х) будет выполняться, начиная со значения х=1. После каждого выполнения цикла х будет увеличиваться на 1, и тело цикла будет выполняться с новым значением х. Цикл будет выполняться до тех пор, пока х не превысит значение z, т. е. 5. Обратите внимание, что после выполнения цикла while значение х будет равно б. Значение у будет равно 120 */ 4.11.5. Оператор do-while Оператор цикла do-while называется оператором цикла с постусловием и ис- пользуется в тех случаях, когда необходимо выполнить тело цикла хотя бы один раз. Синтаксис: do тело while (выражение); Сначала выполняется тело цикла (которое может быть составным операто- ром), затем вычисляется выражение. Если оно истинно (значение выражения от- лично от нуля), то тело цикла выполняется снова и т. д. Если выражение стано- вится ложным (т. е. значение выражения равно нулю), то выполнение оператора do-while заканчивается и выполняется следующий по порядку оператор. Чтобы прервать выполнение цикла до того, как условие станет ложным, мож- но использовать оператор break (см. Оператор break). Пример: /* Оператор do-while */ /* В этом примере вычисляется факториал числа 5 */ void main(void) { int х=1; int у=1; int z=5; do { // Объявляем и инициализируем целую переменную х // Объявляем и инициализируем целую переменную у // Объявляем и инициализируем целую переменную z // Оператор цикла do-while */ У: x: * Тело цикла -У*х; = ++x; // // // вычисляем значение у инкрементируем (увеличиваем на 1) значение х } } while (x<=z); // проверка условия
222 Глава 4. Основы языка Си /* Тело цикла (у=у*х, х=++х) будет выполняться, начиная со значения х=1. После каждого выполнения цикла х будет увеличиваться на 1, затем будет проверяться условие (x<=z), и, если оно истинно, тело цикла будет повторяться с новым значением х. Цикл будет выполняться до тех пор, пока х не превысит значение z, т. е. 5. Обратите внимание, что после выполнения цикла do-while значение х будет равно б. Значение у будет равно 120 */ 4.11.6. Оператор break Оператор break обеспечивает прекращение выполнения самого внутреннего из объединяющих его операторов switch, do-while, for, while. Он даёт возможность управлять выходом из цикла иначе, чем проверкой условия в начале или в конце. После выполнения оператора break управление передаётся оператору, следующе- му за прерванным. Пример: /* Оператор break */ /* Поиск в заданном массиве элемента, равного 0 */ /* Подключим заголовочный файл со стандартными функциями ввода/вывода */ ttinclude <stdio.h> // И int array[7]={-3,-2,-1,0,1,2,3};// И int n=7; // И void main(void) // int i; II 11 11 for(i=0;i<n; i + +) // { /* Тело цикла for */ if(array[i]==0) // break; // } /* Оператор if-else */ if(i==n) // подключаем заголовочный файл co стандартными функциями ввода/вывода Объявляем и инициализируем целочисленный массив array Объявляем и инициализируем целую переменную п — количество элементов массива Основная функция программы Объявляем целую переменную i, которая будет задавать индекс элемента массива array Цикл for Если i-й элемент массива равен 0, то ьыход из цикла for по break Если i=n, printf("%d not found \n",0);// то выводим строку: // "0 not found " else // Если i не равен n, printf("%d on %d place \n",0,i);// то выводим строку: // "0 on i place ", где
4.17. Операторы 223 // i - номер места /* Тело цикла (if(array[i]=0) break) будет выполняться, начиная со значения i=0. После каждого выполнения цикла i будет увеличиваться на 1, затем будет проверяться условие (i<n), и, если оно истинно, тело цикла будет повторяться с новым значением i. Цикл будет выполняться до тех пор, пока i не станет равно п или выражение в теле цикла for не станет истиной, и будет осуществлён выход из цикла по оператору break. В обоих случаях будет осуществлён переход на оператор if-else. */ 4.11.7. Оператор continue Оператор continue прерывает выполнение цикла, но в отличие от оператора break он приводит к началу следующей итерации охватывающего цикла (do-while, for, while). В циклах while и do-while это означает непосредственный переход к вы- полнению проверочной части; в цикле for управление передаётся на шаг измене- ния параметра цикла. Пример: /* Оператор continue */ /* В этом фрагменте будут обрабатываться только положительные элементы массива array */ for (х=0; х<п; х++) // Цикл for { /* Тело цикла for */ if (array[х] < 0) // Если элемент массива меньше 0, continue; // то следующую часть тела цикла пропускаем //и переходим к следующей итерации цикла for /* Здесь производится обработка положительных элементов массива */ [группа операторов] } /* х - индекс элемента массива array[х]. Выполнение цикла for начинается с х=0. Проверяется элемент array[0] массива, и если он меньше 0, то по оператору continue осуществляется переход к следующей итерации цикла for, т. е. х увеличивается на 1 (х++) и проверяется элемент array[1], и т. д. Если элемент массива array[х] больше 0, то оператор continue пропускается и выполняется [группа операторов], которая обрабатывает положительные элементы массива. После выполнения [группы операторов] также осуществляется переход к следующей итерации цикла for и цикл снова повторяется с х=х+1. Цикл for будет выполняться, пока значение х не станет равно (или больше) значению п (условие х<п) */
224 Глава 4. Основы языка Си 4.11.8. Оператор return Оператор return завершает выполнение функции, в которой он задан, и воз- вращает управление в вызывающую функцию, в точку, непосредственно следую- щую за вызовом. Синтаксис: return [выражение] ; Значение выражения, если оно задано, возвращается в вызывающую функ- цию в качестве значения вызываемой функции. Если выражение опущено, то возвращаемое значение не определено. Выражение может быть заключено в круг- лые скобки, хотя их наличие необязательно. Если в какой-либо функции отсутствует оператор return, то передача управле- ния в вызывающую функцию происходит после выполнения последнего операто- ра вызываемой функции. При этом возвращаемое значение не определено. Если функция не должна иметь возвращаемое значение, то её следует объявлять с ти- пом void. Таким образом, использование оператора return необходимо либо для немед- ленного выхода из функции, либо для передачи возвращаемого значения. Пример: /* Оператор return */ /* Функция summa */ int summa (int x, int y) { return (x+y); // Зададим возвращаемое значение } /* Основная функция программы */ void main(void) int а=5, Ь=10; // Объявляем и инициализируем целые // переменные а и b int с; // Объявляем целую переменную с с = summa (а, Ь); // Вычислим значение с через вызов // функции summa /* Функция summa имеет два формальных параметра - х и у типа int, и возвращает значение типа int, о чем говорит описатель, стоящий перед именем функции. После вызова функции summa в основной программе оператор return вернёт значение, равное сумме фактических параметров а и Ь, т. е. с=а+Ь-5+10=15 */ }
4.11. Операторы 225 Пример: /* Оператор return */ #include <stdio.h> // подключаем заголовочный файл co // стандартными функциями ввода/вывода /* Функция print_pozitiv * / void print_pozitiv (int a) { if (a<0) // Если фактический аргумент // отрицательный, то return; // возвращаемся else // Если фактический аргумент // положительный или равен 0, то printf("%d \n", a); // выводим его значение return; } //и возвращаемся /* Основная функция программы */ void main(void) { int x=5; // Объявляем и инициализируем целую // переменную х print_pozitiv (х); // Вызовем функцию print_pozitiv //с фактическим аргументом х=5 [группа операторов] } /* В этом примере оператор return используется для выхода из функции print_pozitiv и возврата в основную функцию программы. При этом возвращаемое значение не определено. В основной программе присваивается значение фактическому аргументу и вызывается функция print_pozitiv. Функция print_pozitiv проверяет значение аргумента, и если оно отрицательное, то по оператору return осуществляется возврат в основную функцию на [группу операторов]. Если значение аргумента неотрицательное, то выводится его значение и также, по оператору return, осуществляется возврат в основную функцию на [группу операторов]. Наличие второго оператора return необязательно */ Оператор return также можно использовать для выхода из оператора switch. Пример: /* Оператор return */ int function(int n,int x) // определение функции function { switch (n) // Оператор switch { case 0: return (1); // Если значение фактического аргумента, // соответствующего формальному и аргументу п, равно 0, то функция
226 Глава 4. Основы языка Си // вернёт значение 1; case 1: return (2); // Если равно 1 - функция вернёт 2; case 2: return (x); // Если 2 - функция вернёт значение // фактического аргумента, // соответствующего формальному // аргументу х case 3: return (x-2); // Если 3 - функция вернёт х-2 default: return (0); //Во всех других случаях // функция вернёт значение 0 4.11.9. Оператор goto Оператор goto передаёт управление на оператор, обозначенный меткой. По- меченный оператор должен находиться в той же функции, что и оператор goto, а используемая метка должна быть уникальной, т. е. одно имя метки нельзя ис- пользовать для разных операторов программы. Синтаксис: goto имя-метки; имя-метки: оператор; С формальной точки зрения оператор goto никогда не является необходимым, и на практике почти всегда можно обойтись без него. Тем не менее есть несколько ситуаций, где оператор goto может найти своё место. Наиболее характерным яв- ляется его использование тогда, когда при выполнении какого-то условия требу- ется прервать выполнение в некоторой глубоко вложенной структуре, например выйти сразу из двух циклов. Здесь нельзя непосредственно использовать опера- тор break, так как он прерывает только самый внутренний цикл. Пример: /* Оператор goto */ for (...) { for (...) { // Первый цикл // Второй цикл if (...) goto Label; // Если условие оператора if выполняется, //то переход на метку Label } Label: // метка Label, после которой [группа операторов] // следуют операторы Метка имеет такую же форму, что и имя переменной, и за ней всегда следует двоеточие. Метка может быть приписана к любому оператору той же функции, в которой находится оператор goto.
4.И. Операторы 227 Использование оператора безусловного перехода goto в практике програм- мирования на языке Си настоятельно НЕ РЕКОМЕНДУЕТСЯ, так как он затрудняет понимание программ и возможность их модификаций. 4.11.10. Оператор-выражение Любое выражение, которое заканчивается точкой с запятой, является опера- тором. Выполнение оператора-выражения заключается в вычислении выражения. Полученное значение выражения никак не используется. Пример: /* Оператор - выражение */ ++х; // Этот оператор-выражение увеличивает значение // переменной х на единицу а=5; // Этот оператор-выражение присваивает переменной //а значение 5 y=function(х); // Этот оператор-выражение включает в себя операции // присваивания (у=...) и вызова функции (function(х)) 4.11.11. Пустой оператор Пустой оператор состоит только из точки с запятой. При выполнении этого оператора ничего не происходит. Синтаксис: / Пустой оператор позволяет поставить метку перед закрывающей скобкой (}) составного оператора или указать пустое тело в операторах цикла, где по синтак- сису требуется хотя бы один оператор. Синтаксис языка Си требует, чтобы после метки обязательно следовал опера- тор. Фигурная же скобка оператором не является. Поэтому, если требуется пере- дать управление на фигурную скобку, следует использовать пустой оператор. Пример: /* Пустой оператор */ int function ( ) { // определение функции function { if (...) // Если условие оператора if выполняется, goto Label; //то осуществляется переход на метку Label, // т. е. на скобку {
228 Глава 4. Основы языка Си } Label:; } // метка Label с пустым оператором, // после которой следует скобка return (...) ; } Пример: /* Пустой оператор */ /* Организация бесконечного цикла с помощью пустого оператора */ while (1) // Условие в скобках всегда истинно { /* Тело цикла while */ ; // Пустой оператор } 4.11.12. Составной оператор Составной оператор представляет собой несколько операторов и объявлений. Для объединения объявлений и операторов в составной оператор или блок ис- пользуются фигурные скобки ({}). Одним из примеров такого типа являются фи- гурные скобки, в которые заключаются операторы, составляющие функцию. Синтаксис: { [объявление] оператор; [оператор]; } После первой фигурной скобки, которая завершает блок (составной опера- тор), в языке Си точка с запятой НИКОГДА не ставится. В Code VisionA VR ДОПУСКАЕТСЯ ставить точку с запятой. Выполнение составного оператора заключается в последовательном выполне- нии составляющих его операторов. Пример: /* Составной оператор */ /* Пример полного определения функции, которая возвращает максимальное из трех значений переменных */ int maximum(int a, int b, int с)// Объявление функции maximum, которая // возвращает целое значение, т. к. перед // перед именем функции стоит int /* Составной оператор */
4.12. Функции 229 int d; d = (a>b)? a : b; return((d>c)? d : c); // Объявление целой переменной d // Переменной d присваивается максимальное //из значений переменных а и b // Функция возвращает максимальное из // значений переменных d и с // (Тернарные операции) 4.12. Функции Выше в этой книге уже упоминалось о функции. В этой главе понятие функ- ции будет рассмотрено подробнее. В отличие от других языков программирования высокого уровня в языке Си нет деления на процедуры, подпрограммы и функции, здесь вся программа стро- ится только из функций. Функция — это совокупность объявлений и операторов, обычно предназна- ченная для решения определённой задачи. С использованием функций в языке Си связаны три понятия: определение функции (описание действий, выполняе- мых функцией), объявление функции (задание формы обращения к функции) и вызов функции. Каждая функция должна иметь имя, которое используется для её объявления, определения и вызова. В любой программе должна быть функция с именем main (главная функция), и именно с этой функции, в каком бы месте программы она ни находилась, начи- нается выполнение программы. Определение функции задаёт тип возвращаемого значения, имя функции, ти- пы и число формальных параметров, а также объявления переменных и операто- ры, называемые телом функции и определяющие действие функции. В определе- нии функции также может быть задан класс памяти. Синтаксис для объявления функции: [<класс памяти>Н<тип>]имя—функции ( [<список параметров:*] { тело функции (список операторов) } Необязательный спецификатор класса памяти задаёт класс памяти функции, который может быть static или extern (подробнее о классах памяти см. Перемен- ные). Спецификатор типа функции задаёт тип возвращаемого значения и может за- давать любой тип. Если спецификатор типа не задан, то предполагается, что функция возвращает значение типа int. Для функций, не возвращающих никако- го значения, должен быть использован тип void, указывающий на отсутствие воз- вращаемого значения. Функция не может возвращать массив или функцию, но может возвращать указатель на любой тип, в том числе и на массив, и на функцию.
230 Глава 4. Основы языка Си Список формальных параметров — это последовательность объявлений фор- мальных параметров, разделённая запятыми. Формальные параметры — это пе- ременные, используемые внутри тела функции и получающие значение при вы- зове функции путем копирования в них значений соответствующих фактических параметров. Список формальных параметров может заканчиваться запятой (,) или запятой с многоточием (,...); это означает, что число аргументов функции пе- ременно. Однако предполагается, что функция имеет, по крайней мере, столько обязательных аргументов, сколько формальных параметров задано перед послед- ней запятой в списке параметров. Такой функции может быть передано большее число аргументов, но над дополнительными аргументами не проводится конт- роль типов. Если тип формального параметра не указан, то этому параметру при- сваивается тип int. Если функция не использует параметров, то наличие круглых скобок обяза- тельно, а вместо списка параметров рекомендуется указать слово void. Тело функции — это составной оператор, содержащий операторы, определяю- щие действие функции. Объявление функции имеет такой же вид, что и определение функции, с той лишь разницей, что тело функции отсутствует и имена формальных параметров тоже могут быть опущены. Чтобы объявить функцию, можно использовать функ- циональные прототипы. Прототип — это явное объявление функции, которое предшествует её определению. Тип возвращаемого значения при объявлении функции должен соответствовать типу возвращаемого значения в определении функции. Пример: /* Прототип функции */ int function(int a, int b, int с)// Объявление функции function, которая // возвращает целое значение, т. к. перед // перед именем функции стоит int /* Фактическое определение функции может быть написано где-нибудь в другом месте программы */ int function(int a, int b, int c) { [группа операторов] // тело функции } При вызове функции ей при помощи аргументов (формальных параметров) могут быть переданы некоторые значения (фактические параметры), используе- мые во время выполнения функции. Функция может возвращать некоторое (но только одно!) значение. Это возвращаемое значение и есть результат выполнения функции, который при выполнении программы подставляется в точку вызова функции, где бы этот вызов ни встретился. Допускается также использовать функ- ции, не имеющие аргументов, и функции, не возвращающие никаких значений. Действие таких функций может состоять, например, в изменении значений неко- торых переменных, выводе некоторых значений и т. п.
4.12, Функции 231 Пример: /* Вызов функции */ /* определение функции с формальными параметрами а, Ь, и с */ int function(int a, int b, int c) { return (a+b+c); // тело функции } /* Основная функция программы */ void main(void) { int x=5, y=6, z=10; // Объявляем и инициализируем целые // переменные х, у и z int result; // Объявляем целую переменную result result=function(х,у,z); // Вызовем функцию function с фактическими // параметрами х,у и z. В результате функция // вернёт значение суммы этих параметров: // result=5+6+10=21 } В CodeVisionAVR не поддерживается так называемый старый стиль написа- ния определений функций, при котором описание формальных параметров рас- положено между именем функции со списком формальных параметров и телом функции. Пример: /* определение функции в "старом стиле", которое CodeVisionAVR не поддерживает. Будет выдаваться синтаксическая ошибка!!! */ int maximum(а, b, с) int а, Ь, с; { [группа операторов] // тело функции } /* Правильное определение функции */ int function(int a, int b, int c) { [группа операторов] // тело функции } Параметры функций передаются через Data Stack (Стек данных). Значения функций возвращаются в регистры R30, R31, R22 и R23 (от младше- го байта (LSB) к старшему (MSB)).
232 Глава 4. Основы языка Си 4.13. Указатели Указатель — это переменная, содержащая адрес другой переменной. При объявлении переменной типа указатель необходимо определить тип объекта дан- ных, адрес которых будет содержать переменная, и имя указателя с предшествую- щей звездочкой (*). Синтаксис для объявления указателя: ^модификатор места хранения переменной>] type * ^модификатор места хранения указателя>] имя_указателя; ИЛИ type ^модификатор места хранения переменной>] * ^модификатор места хранения указателя^ имя_указателя; где type может быть любым типом данных (int, char, struct и т. д.). Пример: unsigned int * a; // переменная а представляет собой указатель //на переменную типа unsigned int (целое без знака) char * x; // переменная х представляет собой указатель // на переменную типа char int nomer; // Объявляем целую переменную nomer int *addres; // Переменную addres объявляем как указатель, addres = & nomer; //ей присваиваем адрес переменной nomer // (& - операция вычисления адреса) int x,y; // Объявляем целые переменные х и у int *point_x; // Переменная point_x объявлена как указатель, point_x = &x; //ей присваивается адрес переменной х, у = *point_x; // Переменной у присваивается значение х, это // равносильно оператору: у = х; Из-за Гарвардской архитектуры микроконтроллеров AVR, с раздельным про- странством адресов для данных SRAM, программы FLASH- и EEPROM-памяти, компилятор CodeVisionAVR имеет три типа указателей. Для доступа к переменным, расположенным в SRAM, используются обычные указатели. Для доступа к константам, расположенным во FLASH-памяти, используется модификатор типа места хранения flash. Для доступа к переменным, расположенным в EEPROM, используется моди- фикатор типа места хранения eeprom. Хотя указатели могут указывать на различные области памяти, по умолчанию они хранятся в SRAM. Пример: /* Указатель на символьную строку, расположенную в SRAM */ char *point_to_sram = "String in SRAM";
4.13. Указатели 233 /* Указатель на символьную строку, расположенную во FLASH */ flash char *point_to_flashl = "Stringl in FLASH"; char flash *point_to_flash2 = "String2 also in FLASH"; /* Указатель на символьную строку, расположенную в EEPROM */ eeprom char *point_to_eepl = "Stringl in EEPROM"; char eeprom *point_to_eep2 = "String2 also in EEPROM"; Для того чтобы сохранить сам указатель в других областях памяти, типа FLASH или EEPROM, должны быть использованы модификаторы хранения ука- зателя flash или eeprom. Пример: /* Указатель - во FLASH, символьная строка - в SRAM */ char *flash flash_point_to_sram = "String in SRAM"; /* Указатель - во FLASH, символьная строка - во FLASH */ flash char *flash flash_point_to_flash = "String in FLASH"; /* Указатель - во FLASH, символьная строка - в EEPROM */ eeprom char *flash flash_point_to_eep = "String in EEPROM"; /* Указатель - в EEPROM, символьная строка - в SRAM */ char *eeprom eep_point_to_sram = "String in SRAM"; /* Указатель - в EEPROM, символьная строка - во FLASH */ flash char *eeprom eep_point_to_flash = "String in FLASH"; /* Указатель - в EEPROM, символьная строка - в EEPROM */ eeprom char *eeprom eep_point_to_eep = "String in EEPROM"; Для повышения эффективности кода в CodeVisionAVR реализованы две моде- ли памяти: • модель памяти TINY использует 8 битов для хранения указателей перемен- ных, размещённых в SRAM. В этой модели памяти можно иметь доступ только к первым 256 байтам SRAM; • модель памяти SMALL использует 16 битов для хранения указателей пере- менных, размещенных в SRAM. В этой модели памяти можно иметь доступ к 65536 байтам SRAM. Для повышения скорости и уменьшения размера программы следует пытаться всегда использовать модель памяти TINY. Указатели на области памяти FLASH и EEPROM всегда используют 16 битов. Поскольку указатели во FLASH-памяти используют 16 битов, общий размер массива констант и символьных строк не может превышать 64 К для ATmegalO3 или ATmegal28. Тем не менее общий размер программы для этих чипов может быть 128 К. Указатели могут быть сгруппированы в массивы, которые могут иметь до 8 из- мерений.
234 Глава 4. Основы языка Си Пример: /* Глобальные массивы указателей */ /* Объявим и инициализируем глобальный массив указателей на символьные строки, размещённые в SRAM. Сам массив указателей - в SRAM */ char *array_point[4] = {"Str_sraml","Str_sram2","Str_sram3","Str_sram4"}; /* Объявим и инициализируем глобальный массив указателей на символьные строки, размещённые во FLASH. Сам массив указателей - во FLASH */ flash char *flash array_point_flash[2] = {"Str_flashl","Str_flash2"}; /* Объявим и инициализируем глобальный массив указателей на символьные строки, размещенные в EEPROM. Сам массив указателей - в EEPROM */ eeprom char *eeprom array_point_eep[3] = {"Str_eepl","Str_eep2","Str_eep3"} Пример: /* Локальные массивы указателей */ /* Объявим и инициализируем несколько строк в EEPROM */ eeprom char strl[]="Stringl"; eeprom char str2[]="String2"; eeprom char str3[]="String!"; /* Основная функция программы */ void main(void) { /* Объявим локальный массив указателей на символьные строки, размещённые в EEPROM. Сам массив указателей - в SRAM */ char eeprom *local_array_point[3]; /* и инициализируем массив */ local_array_point[0] = strl; local_array_point[1] = str2; local_array_point[2] = str3; } Указатели функций всегда занимают 16 битов, поскольку они используются для доступа к области FLASH-памяти. Для этих типов указателей нет необходи- мости использовать ключевое слово flash.
4.14. Доступ к регистрам ввода/вывода 235 Пример: /* Указатели функций */ /* Объявим и определим функцию summa */ int summa(int x, int y, int z) { return (x+y+z); } /* Объявим и инициализируем глобальный указатель функции summa */ int (*sum_ptr) (int x, int y, int z)=summa; /* Основная функция программы */ void main(void) { int i ; i=(*sum_ptr) (5,12,7); // Вызовем функцию summa с фактическими // аргументами 5, 12 и 7, используя указатель. // Функция вернёт сумму этих значений. // В результате i=5+12+7=24 } 4.14. Доступ к регистрам ввода/вывода Каждому порту в микроконтроллере AVR соответствуют минимум три регист- ра: • DDRx — регистр направления данных (х — буква соответствующего порта, например DDRB — регистр направления данных Порта В). Этот регистр определяет, как должен быть сконфигурирован вывод порта — как вход или как выход. Если в бит этого порта записать 1, то соответствующий вы- вод порта будет работать как выход; если записать 0, то соответствующий вывод порта будет работать как вход. Например, если в регистре DDRB за- писано число 01000011, то это значит, что 0-й, 1-й и 6-й выводы Порта В сконфигурированы как выходы, а остальные — как входы. • PORTx — выходной регистр порта х (например, PORTB — выходной ре- гистр Порта В). В биты этого регистра записывают те значения, которые хотят получить на выводах порта, при условии, что выводы сконфигуриро- ваны как выходы. Если вывод сконфигурирован как вход, то при записи в соответствующий бит регистра PORTx 1 этот вывод становится «входом с подтяжкой», т. е. этот вывод через резистор примерно 100...150 кОм, находящийся внутри микроконтроллера, подключается («подтягивается») к напряжению пита- ния микроконтроллера. Таким образом, при отсутствии сигнала на этом входе будет уровень логической 1. • PINx — выводы порта х (например, PINB — выводы Порта В). Этот ре- гистр содержит значения логических уровней, которые к настоящему вре- мени присутствуют на физических (т. е. реальных) выводах порта. Этот ре- гистр доступен ТОЛЬКО для чтения.
236 Глава 4. Основы языка Си Для доступа к регистрам ввода/вывода микроконтроллера AVR с помощью инструкций ассемблера IN и OUT компилятор использует ключевые слова sfrb и sfrw. Пример: /* Доступ к регистрам ввода/вывода */ /* Определим SFR (Регистры общего назначения) для чипа AT90S8515 */ sfrb PINC=0xl3; И Так как регистр PINA 8-битный, то к нему осуществляется II 8-битный доступ с помощью sfrb (sfr byte) II 0x13 - это адрес порта PINC sfrw TCNTl=0x2c; II так как регистр TCNT1 16-битный, то к нему II осуществляется 16-битный доступ с и помощью sfrw (sfr word) II 0x2с - это адрес регистра TCNT1 /* Основная функция программы */ void main(void) { unsigned char x; // Объявляем символьную переменную x x=PINC; // Чтение выводов Порта А TCNTl=0xF4A6; // Запись в регистры TCNT1L & TCNT1H } Адреса регистров ввода/вывода определены в следующих заголовочных фай- лах, расположенных в поддиректории ..\INC: tinyl3.h 90s8534.h тедабОЗ.h tiny22.h 90s8535.h mega64.h tiny26.h megalO3.h mega8.h 90s2313.h megal28.h mega8515.h 90s2323.h megal6.h mega8535.h 90s2333.h megal61.h 43usb355.h 90s2343.h megal62.h 76c711.h 90s4414.h megal63.h 86rf401.h 90s4433.h megal69.h 94k.h 90s4434.h mega32.h 90s8515.h mega323.h В начале программы посредством #include можно подключить соответствую- щий файл для того микроконтроллера, который используется в данном проекте.
4.14. Доступ к регистрам ввода/вывода 237 4.14.1. Побитовый доступ к регистрам ввода/вывода Побитовый доступ к регистрам ввода/вывода осуществляется путем добавле- ния выбранных битов после имени регистра. Поскольку побитовый доступ к регистрам ввода/вывода осуществляется с ис- пользованием инструкций CBI, SBI, SBIC и SBIS, адрес регистра должен быть в диапазоне 0...1 РИдля sfrb и в диапазоне 0...1Eh для sfrw. Пример: /* Доступ к регистрам ввода/вывода */ /* Определим SFR (Регистры общего назначения) для чипа AT90S2313 */ sfrb PORTB=Oxl8; // Объясняем компилятору, по каким адресам находятся sfrb DDRB=0xl7; //те или иные регистры (порты). Так как все используемые sfrb PINB=0xl6; // регистры 8-битные, то к ним осуществляется // 8-битовый доступ с помощью sfrb. Обычно эти // операторы располагаются в заголовочных файлах // (см. Доступ к регистрам ввода/вывода) int а, Ь, с; // Объявляем целые переменные а, Ь, с /* Основная функция программы */ void main(void) { /* Зададим направление выводов Порта В */ DDRB.0=0; DDRB.1=O; DDRB.2=0; DDRB.3=0; // зададим биты 0...3 И 11 II Порта В как входы DDRB.4=1; II зададим биты 4...7 Порта В как выходы DDRB.5=1; И DDRB.6=1; И DDRB.7=1; И /* Установим выводы Порта В */ PORTB.0=0; PORTB.1=0; // установим биты 0 и 1 Порта В как // простые входы PORTB.2=1; // установим биты 2 и 3 Порта В как "входы с PORTB.3=1; // подтяжкой", т. е. каждый из этих выводов через // резистор примерно 100...150 кОм будет соединён //с шиной питания. Таким образом, в отсутствие // сигнала на этих выводах будет уровень // логической 1 PORTB.4=0; // установим биты 4 и 5 Порта В как выходы, на PORTB.5=0; // которых установлен уровень логического 0
238 Глава 4. Основы языка Си PORTB.6=1; // установим биты 6 и 7 Порта В как выходы, на PORTB.7=1; // которых установлен уровень логической 1 /* опросим выводы Порта В, т. е. их физические значения */ a=PINB.O; // Переменной а присвоим значение вывода 0 // Порта В, т. е. значение сигнала, на выводе 0 b=PINB.5; // Переменной // Порта В, т. b присвоим значение вывода 5 е. Ь=0 c=PINB.6; // Переменной с присвоим значение вывода б // Порта В, т. е. с=1 if (PINB.2) // Если на выводе 2 Порта В логическая 1, { //то выполняется [группа операторов!]; [группа операторов!] } Else // если 0 - то { [группа операторов2] } [группа операторов2] Чтобы улучшить удобочитаемость программы, битам в регистрах ввода/выво- да можно присвоить символические имена посредством #define: Пример: /* Присвоение символических имен битам регистров ввода/вывода */ /* Эта примитивная программа зажигает светодиод по нажатию кнопки. Чип: AT90S2313. Светодиод подключён катодом к выводу 0 Порта В, а анодом через резистор 470 Ом - к плюсу источника питания. Кнопка одним выводом подключена к выводу 1 Порта В, а другим - к минусу источника питания (к "общему"). Частота кварца 1 МГц, хотя в данном случае она может быть любой */ #include <90s2313.h> // подключаем файл, где определены адреса регистров /* Присвоение символических имен */ #define output_led PORTB.0 // Выход на светодиод #define input_button PINB.l // Вход кнопки /* Основная функция программы */ void main(void) { /* Зададим направление выводов Порта В */ DDRB.0=1; // зададим бит 0 Порта В как выход DDRB.1=0; // зададим бит 1 Порта В как вход /* Установим выводы Порта В */ output_led =1; // используем символическое имя бита PORTB.0, // запишем в него 1, чтобы установить вывод 0
4.15. Доступ к EEPROM-памяти 239 // Порта В как выход, на котором установлен уровень // логической 1, чтобы светодиод не горел PORTB.1=1; // установим бит 1 Порта В как "вход с подтяжкой", // чтобы при отжатой кнопке на этом выводе была 1, а // при нажатии кнопки уровень менялся на О /* Бесконечный цикл */ while (1) // { /* Тело цикла while */ if (input_button==0) // И 11 output_led=0; // else // output_led=l; // } // } // Оператор цикла В условии используем символическое имя вывода PINB.1. Если кнопка нажата, т. е. на выводе PINB.1 логический 0, то зажигаем светодиод Иначе, если кнопка отжата, гасим светодиод Скобка закрывает цикл while() Скобка закрывает функцию main() Результат выполнения этой программы представлен на Рис. 4.4. Цепь сброса, питания и кварцевый резонатор микроконтроллера не показаны (см. Рис. 4.1). Рис. 4.4. Результат работы программы — нажатие кнопки вызывает зажигание светодиода. VCC Важно отметить, что побитовый доступ для регистров ввода/вывода, рас- положенных во внутреннем SRAM выше адреса 5Fh (например, Порт F для ATmegal28), РАБОТАТЬ НЕ БУДЕТ, поскольку ассемблерные инструкции CBI, SBI, SBIC и SBIS не могут быть использованы для доступа к SRAM. 4.15. Доступ к EEPROM-памяти Каждый микроконтроллер AVR содержит электрически стираемую энергоне- зависимую память (EEPROM). EEPROM организована как отдельная область данных, каждый байт которой может быть прочитан и перезаписан. EEPROM вы- держивает не менее 100000 циклов записи/стирания.
240 Глава 4. Основы языка Си Доступ к внутреннему EEPROM AVR осуществляется путём использования глобальных переменных, следующих за ключевым словом eeprom. Пример: /* Глобальные переменные, хранящиеся в EEPROM */ eeprom int var_eep=5; // Значение 5 запишется в EEPROM в // процессе программирования чипа eeprom long array_eep[8]; // Объявление массива длинных целых переменных eeprom char var_eepl; // Объявление символьной переменной var_eepl eeprom char string[]="Hello"; ;// Строка (символьный массив) запишется в // EEPROM в процессе программирования чипа Пример: /* Использование EEPROM в программе */ eeprom int var_eep; // Объявление целой переменной var_eep, // хранящейся в EEPROM /* Основная функция программы */ void main(void) { int i; II Объявление целой переменной i, хранящейся //в SRAM int eeprom *point_to_eep; // объявление указателя EEPROM // сам указатель хранится в SRAM /* Непосредственная запись значения 98 в EEPROM */ var_eep=98; /* Косвенная запись того же значения с использованием указателя */ point_to_eep=&var_eep; *point_to_eep=98; /* Непосредственное чтение значения из EEPROM */ i=var_eep; /* Косвенное чтение того же значения с использованием указателя */ i=*point_to_eep; } Указатели на EEPROM ВСЕГДА используют 16 битов.
4.16. Использование прерываний 241 4.16. Использование прерываний Прерывание является механизмом изменения последовательности выполне- ния программы. При работе в приложениях микроконтроллер взаимодействует со многими периферийными устройствами, которые в произвольные моменты времени, возможно даже одновременно, требуют его реакции. Вместо поочередного опроса каждого периферийного устройства, не нужда- ются ли они в обслуживании, можно заставить их самих делать вызов, когда им это потребуется. Этот процесс называется прерыванием, т. к. периферийное уст- ройство (например, нажатая кнопка) прерывает основное выполнение програм- мы. После этого микроконтроллер останавливает нормальное выполнение про- граммы, определяет источник прерывания и выполняет необходимое действие, т. е. обслуживает прерывание. Затем микроконтроллер продолжает нормальное выполнение программы. В CodeVisionAVR для обслуживания прерывания служит соответствующая функция, т. е. при запросе прерывания вызывается функция обслуживания этого прерывания. В первую очередь обрабатываются прерывания, имеющие более высокий приоритет. Номера векторов прерываний для конкретного микроконтроллера приведены в соответствующем заголовочном файле (см. Доступ к Регистрам Вво- да/Вывода). Чем меньше номер вектора прерывания, тем выше его приоритет. Подробнее о сиситеме прерываний смотрите описание на конкретный мик- роконтроллер AVR (см. Команда Help -» AVR Data Sheets (Помощь -» Описания AVR)). Доступ к системе прерываний AVR осуществляется с помощью ключевого слова interrupt (прерывание). Номера векторов прерывания начинаются с 1, но в заголовочном файле нет вектора с таким номером. Дело в том, что этот номер принадлежит прерыванию с самым высоким приоритетом, а именно RESET (Сброс). Так как при аппаратном сбросе микроконтроллера выполнение программы ВСЕГДА начинается с функ- ции main, то смысл в обработке этого прерывания пропадает. Функция обработки прерывания имеет следующий синтаксис: interrupt [номер_вектора_прерывания] имя_функции_обработки_прерывания ([пара- метры] ) { [тело функции] } Функции прерывания не могут возвращать значения, не имея параметров. При вызове функции обработки прерывания компилятор автоматически со- хранит все используемые регистры и восстановит их при выходе обратно. В конце функции прерывания устанавливается ассемблерная инструкция RETI. Пример: /* Функции обработки прерываний для AT90S2313 */ /* Функция обработки прерывания по аналоговому компаратору */
242 Глава 4. Основы языка Си interrupt { [11] void analog_comp(void) // при срабатывании компаратора автоматически будет вызвана эта функция, т. к. в квадратных скобках стоит число И } /* Тело [ группа функции */ II операторов!] // // 11, которое соответствует номеру вектора прерывания по аналоговому компаратору ANA_COMP (см. файл 90s2313.h). Вместо 11 в квадратных скобках можно было написать ANA_COMP // И II И И /* Функция обработки прерывания по переполнению таймера/счётчика 0 */ interrupt { /* Тело [группа [7] void overflow_timerO(void)// // функции */ // операторов2] // // // // И II И при переполнении таймера/счётчика 0 автоматически будет вызвана эта функция, т. к. в квадратных скобках стоит число 7, которое соответствует номеру вектора прерывания по переполнению таймера/счётчика 0 TIMO_OVF (см. файл 90s2313.h). Вместо 7 в квадратных скобках можно было написать TIMO_OVF /★ При одновременном запросе этих прерываний в первую очередь будет обрабатываться прерывание по переполнению таймера/счётчика О, т. к. номер его вектора (7) меньше, чем у прерывания по аналоговому компаратору(11), следовательно, оно имеет более высокий приоритет */ Разумеется, чтобы сконфигурировать систему прерывания и разрешить пре- рывания, необходимо также установить соответствующие биты в управляющих регистрах. Подробнее смотрите описание на конкретный микроконтроллер AVR (см. Команда Help -у AVR Data Sheets (Помощь -» Описания AVR)). Пример: /* Конфигурирование и разрешение прерываний */ /* Разрешим прерывание по переполнению таймера/счётчикаО */ TIMSK=0x02; /* Разрешим прерывание по переключению выхода аналогового компаратора */ ACSR=0x08; /* Глобальное разрешение прерываний */ #asm("sei") // здесь используется ассемблерная инструкция sei, которая // устанавливает флаг глобального прерывания (I) //в регистре статуса (SREG) По умолчанию в течение прерываний разрешено автоматическое сохранение и восстановление регистров RO, Rl, R15, R22...R27, R30, R31 и SREG, т. е. при за-
4.17. Организация памяти SRAM 243 просе какого-либо прерывания значения этих регистров сохраняются в стеке, а по окончании обслуживания прерывания значения этих регистров извлекаются из стека и восстанавливаются. Автоматическое сохранение и восстановление этих регистров в течение пре- рываний можно включить или выключить, используя директиву #pragma savereg (см. Директива ^pragma savereg). Пример: /* Автоматическое сохранение и восстановление регистров */ ttpragma savereg- // Выключим автоматическое сохранение регистров /* Функция обработки прерывания по переполнению таймера/счётчикаО */ interrupt [7] void overflow_timerO(void) { /* теперь сохраним только те регистры, которые используются программами в обработчике, например R22, R23 и SREG */ /* Этот кусок написан на ассемблере */ #asm push r22 push r23 in r22,SREG push r22 #endasm /* Здесь располагается код Си обработчика прерывания */ /* теперь восстановим SREG, R23 и R22 */ /* Этот кусок написан на ассемблере */ #asm pop r22 out SREG,r22 pop r23 pop r22 #endasm } /* снова разрешим автоматическое сохранение и восстановление регистров для других обработчиков прерываний */ #pragma savereg+ 4.17. Организация памяти SRAM Скомпилированная программа имеет карту памяти, показанную на Рис. 4.5. Область Рабочие Регистры содержит 32 8-битных рабочих регистров общего назначения R0...R31. Компилятор CodeVisionAVR использует следующие регист- ры: RO, Rl, R15, R22...R31. Также некоторые регистры (R2...R14) могут быть рас- пределены компилятором для глобальных битовых переменных. Остальная часть
244 Глава 4. Основы языка Си неиспользованных регистров в этом дипазоне распределена для глобальных char (символьных) и int (целых) переменных. Регистры R16...R21 распределены для локальных char (символьных) и int (целых) переменных. О Рабочие регистры 20h Регистры ввода/вывода DSTACKEND Стек данных (Data Stack) 60h бОИ+Размер стека данных Глобальные переменные HSTACKEND Аппаратный стек 60h + Размер стека данных + Размер глобальных переменных КонецЭРАМ Рис. 4.5. Карта памяти скомпилированной программы. Область Регистры ввода/вывода содержит 64 адреса для периферийных функ- ций CPU, таких как Port Control Registers (Порт управления регистрами), Timer/Counters (таймеры/счётчики) и другие функции ввода/вывода. Можно сво- бодно использовать эти регистры в ассемблерных программах. Область Стек данных (Data Stack) используется для динамического хранения локальных переменных, передачи параметров функции и хранения регистров R0, Rl, R15, R22...R31 и SREG в течение обслуживания прерывания. Для Указателя стека данных (Data Stack Pointer) используется регистр Y. При запуске программы Указатель стека данных инициализируется со значением бОИ+Размер стека данных. При каждой записи значения в Стек данных Указатель стека данных уменьша- ется на единицу. При каждом извлечении значения Указатель стека данных обрат- но увеличивается на единицу. При конфигурировании проекта (см. Закладка С Compiler (Компилятор Си)) следует определить достаточный Data Stack Size (Размер стека данных) так, что- бы он в течение выполнения программы не перекрыл область Регистров вво-
4.17. Организация памяти SRAM 245 да/вывода. Иначе, CodeVisionAVR при компиляции (см. Компиляция проекта) или при построении проекта (см. Построение проекта) выдаст предупреждение (Рис. 4.6). Л The estimated Data Stack usage of 28 byte(s) У \ exceeds the ammount allocated for this purpose in the -----1 C Compiler configuration. It is recommended to increase the Data Stack size in the Project|Configure|C Compiler|Code Generation menu. Puc. 4.6. Окно Warning (Предупреждение), появляющееся при недостаточном Размере стека данных (Data Stack Size). В этом окне говорится буквально следующее: Оценочное использование стека данных 28 Б превышает размер, заданный для этой цели в конфигурации проекта на закладке С Compiler (Компилятор Си). Рекомендуется увеличивать размер Стека данных в меню Project -> Configure —> С Compiler —> Code Generation (Проект Конфигурация —> Компилятор Си —> Генерация кода). Область Глобальные переменные используется для статического хранения гло- бальных переменных в течение выполнения программы. Размер этой области может быть вычислен суммированием размеров всех объявленных глобальных переменных. Область Аппаратный Стек (Hardware Stack) используется для хранения адресов возврата функций. Регистр SP (Stack Pointer — Указатель стека) используется как указатель стека и инициализируется при запуске программы со значением последнего (самого старшего) адреса SRAM. В течение выполнения программы Аппаратный стек растет вниз, в область Глобальных переменных. В конфигурации проекта (см. Закладка С Compiler (Компилятор Си)) есть оп- ция Stack End Markers (Отмечать конец стека), при выборе которой компилятор будет помещать строки DSTACKEND (Конец стека данных) или соответственно HSTACKEND (Конец аппаратного стека) в конец области Стека данных (Data Stack) или соответственно Аппаратного стека (Hardware Stack), как показано на Рис. 4.5. Если при отладке программы в AVR Studio будет видно, что эти строки пере- писываются, то в конфигурации проекта, в поле SRAM (ОЗУ), следует увеличить значение Data Stack Size (Размер стека данных). Если программа работает корректно, можно запретить опцию Stack End Markers (Отмечать конец стека), чтобы уменьшить размер кода.
246 Глава 4. Основы языка Си 4.18. Использование внешнего файла запуска В каждой программе компилятор Си CodeVisionAVR автоматически генериру- ет кодовую последовательность, чтобы сразу после сброса чипа AVR выполнить следующие инициализации: 1) задать таблицу векторов прерываний; 2) запретить глобальные прерывания; 3) запретить доступ к EEPROM; 4) запретить Watchdog (Сторожевой таймер); 5) разрешить, если необходимо, состояние ожидания и доступ к внешней SRAM; 6) очистить регистры R2...R14; 7) очистить SRAM; 8) инициализировать глобальные переменные, расположенные в SRAM; 9) инициализировать Указатель стека данных (Data Stack Pointer) — регистр Y; 10) инициализировать Указатель стека (Stack Pointer) — регистр SP; 11) инициализировать, если необходимо, регистр UBRR. Автоматическую генерацию кодовых последовательностей со 2-й по 8-ю мож- но запретить, выбрав при конфигурировании проекта (см. Закладка С Compiler (Компилятор Си)) опцию Use an External Startup Initialization File (Использовать внешний запускающий файл инициализации). После этого компилятор включит в генерируемый .asm файл кодовые последовательности из внешнего файла, ко- торый должен быть назван STARTUP.ASM. Этот файл должен быть расположен в директории, где находится исходный .с файл. Можно написать собственный файл STARTUP.ASM, чтобы изменить или до- бавить некоторые характеристики к разрабатываемой программе. После того как чип будет сброшен, сразу будут выполнены кодовые последовательности из этого файла. Основной файл STARTUP.ASM поставляется с дистрибутивом компилятора и расположен в директории .. \BIN. Вот содержимое этого файла с русифицированными комментариями: /CodeVisionAVR С Compiler ; (С) 1998-2002 Pavel Haiduc, HP InfoTech s.r.l. /ПРИМЕР ФАЙЛА STARTUP ДЛЯ CodeVisionAVR VI.24.lx ИЛИ БОЛЕЕ ПОЗДНЕЙ .EQU CLEAR_START=0X60 .•НАЧАЛЬНЫЙ АДРЕС ОБЛАСТИ SRAM ДЛЯ ОЧИСТКИ .•УСТАНОВИТЬ ЭТОТ АДРЕС В 0X100 ДЛЯ ;АТтеда12 8 ИЛИ АТтеда64 ЧИПОВ .EQU CLEAR_SIZE=256 .•РАЗМЕР ОЧИЩАЕМОЙ ОБЛАСТИ SRAM В БАЙТАХ CLI .•ЗАПРЕЩАЕМ ПРЕРЫВАНИЯ CLR R30 OUT EECR.R30 .•ЗАПРЕЩАЕМ ДОСТУП К EEPROM /ЗАПРЕЩАЕМ WATCHDOG (СТОРОЖЕВОЙ ТАЙМЕР) LDI R31,0xl8 OUT WDTCR,R31
4.18. Использование внешнего файла запуска 247 OUT WDTCR,R30 OUT MCUCR,R30 ;MCUCR=0, ЗАПРЕЩАЕМ ДОСТУП К ВНЕШНЕМУ SRAM /ОЧИЩАЕМ R2-R14 LDI R24,13 LDI R26,2 CLR R27 __CLEAR_REG: ST X+,R30 DEC R24 BRNE ___CLEAR_REG /ОЧИЩАЕМ SRAM LDI R24,LOW(___CLEAR_SIZE) LDI R25,HIGH(___CLEAR_SIZE) LDI R26,LOW(___CLEAR_START) LDI R27,HIGH(___CLEAR_START) __CLEAR_SRAM: ST X+,R30 SBIW R24,l BRNE ___CLEAR_SRAM /ИНИЦИАЛИЗИРУЕМ ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ LDI R30,LOW(___GLOBAL_INI_TBL*2) LDI R31,HIGH(___GLOBAL_INI_TBL*2) __GLOBAL_INI_NEXT: LPM ADIW R30,l MOV R24,R0 LPM ADIW R30,l MOV R25,R0 SBIW R24,0 BREQ ___GLOBAL_INI_END LPM ADIW R30,l MOV R26,R0 LPM ADIW R30,l MOV R27,R0 LPM ADIW R30,l MOV R1,RO LPM ADIW R30,l MOV R22,R30 MOV R23,R31 MOV R31,R0 MOV R3O,R1
248 Глава 4. Основы языка Си _GLOBAL_INI_LOOP: LPM ADIW R30,l ST X+,RO SBIW R24,l BRNE _GLOBAL_INI_LOOP MOV R30,R22 MOV R31,R23 RJMP _GLOBAL_INI_NEXT __GLOBAL_INI_END: Константы___CLEAR_START и_____CLEAR-SIZE можно изменить, чтобы оп- ределить, какую область SRAM очистить в программе инициализации. Метка__GLOBAL-INI-TBL должна быть расположена в начале таблицы, содер- жащей информацию, необходимую для инициализации глобальных переменных, расположенных в SRAM. Эта таблица генерируется компилятором автоматически. 4.19. Включение в программу ассемблерного кода В любом месте программы можно включить ассемблерный код, используя ди- рективы #asm и #endasm (см. Директивы #asm и ttendasm). В ассемблерных подпрограммах можно свободно использовать регистры R0, Rl, R22...R27, R30 и R31. Однако если эти регистры также используются в функ- ции обслуживания прерывания, то необходимо сохранить их значения при входе в эту функцию и соответственно восстановить их при выходе из этой функции (см. Использование прерываний). 4.19.1. Вызов ассемблерных функций из Си Следующий пример показывает, как из программы Си осуществить доступ к функциям, написанным на языке ассемблера. Пример: // Доступ к функциям, написанным на языке ассемблера // Тело функции написано на ассемблере // эта функция возвращает x+y+z ttpragma warn- // запретим предупреждения int summa(int x, int y, int z) { #asm Id r30,Y ; В регистре R30 младший байт (LSB) переменной z Idd r31,Y+l ; В регистре R31 старший байт (MSB) переменной z Idd r26,Y+2 ; В регистре R26 - LSB переменной у Idd r27,Y+3 ; В регистре R27 - MSB переменной у add r30,r26 ; В регистрах (R31,R30)=z+y adc r31,r27 Idd r26,Y+4 ; В регистре R26 - LSB переменной х Idd r27,Y+5 ; В регистре R27 - MSB переменной х
4.19. Включение в программу ассемблерного кода 249 add г30,г2б adc r31,r27 ; В регистрах (R31,R30)=(R31,R30)+х ttendasm #pragma warn+ // разрешим предупреждения /* Основная функция программы */ void main(void) { int result; // теперь вызовем функцию и сохраним результат в переменной result result=summa(12,34,56); Обратите внимание, что комментарии в ассемблерной части кода отде- ляются знаком точка с запятой (;). Иначе будет выдаваться ошибка при ассемблировании (при построении проекта)! Компилятор передаёт параметры функции, используя Стек данных (Data Stack). Указатель Стека данных хранится в парном регистре Y (R28, R29). Сначала компилятор помещает параметр х, затем у и, наконец, параметр z. При каждом помещении параметра в Стек данных парный регистр Y (указатель Стека данных) уменьшается на размер этого параметра в байтах (на 4 для long int (длинного целого), на 2 для int (целого), на 1 для char (символа)). Для многобайтных параметров в Стек данных сначала помещается MSB (стар- ший байт), а затем LSB (младший байт). Как видно, Стек данных растёт вниз. После помещения всех параметров функций в Стек данных регистр Y указыва- ет на младший байт последнего параметра г, так что функция читает его значение в регистры R30 (LSB) и R31 (MSB), используя инструкции ассемблера: Id г30,Y ; Загрузить в регистр R30 значение, находящееся в Стеке Idd r31,Y+l ; данных по указателю Y (младший байт z) ; Загрузить в регистр R31 значение, находящееся в Стеке ; данных по указателю Y+1 (старший байт z) MSB был помещён первым, так что он находится по более высокому адресу. Переменную z загружаем в регистры, в которых будет конечный результат, т. е. в регистры, в которые функция возвращает своё значение (см. ниже). Параметр у был помещен перед z, так что он находится в Стеке данных по бо- лее высокому адресу. Функция читает его значение в регистры R26 (LSB) и R27 (MSB), используя инструкции ассемблера: Idd r26,Y+2 ; Загрузить в регистр R26 значение, находящееся в Стеке Idd r27,Y+3 ; данных по указателю Y+2 (младший байт у) ; Загрузить в регистр R27 значение, находящееся в Стеке ; данных по указателю Y+3 (старший байт у) Теперь сложим побайтно переменные у и z:
250 Глава 4. Основы языка Си add г30,г2б ; Сложить содержимое регистров R30 и R26 (младшие ; байты переменных z и у) и результат поместить в R30 adc r31,r27 ; Сложить содержимое регистров R31 и R27 (старшие ; байты переменных z и у) с переносом, который может ; образоваться при сложении младших байтов, и результат ; поместить в R31 ; В результате в регистрах (R31,R30)=z+y Параметр х был помещён перед у, так что он находится по ещё более высокому адресу в Стеке данных. Функция записывает его значение в регистры R26 (LSB) и R27 (MSB): Idd r26,Y+4 ; Загрузить в регистр R26 ; данных по указателю Y+4 значение, находящееся в Стеке (младший байт х) Idd r27,Y+5 ; Загрузить в регистр R27 ; данных по указателю Y+5 значение, находящееся в Стеке (старший байт х) Теперь сложим побайтно (y+z) и переменную х: add г30,г2б ; Сложить содержимое регистров R30 и R26 (младшие ; байты (y+z) и х) и результат поместить в R30 adc r31,r27 ; Сложить содержимое регистров R31 и R27 (старшие ; байты (y+z) их) с переносом, который может ; образоваться при сложении младших байтов, и результат ; поместить в R31 ; В результате в регистрах (R31,R30)=z+y+x Функции возвращают свои значения в следующие регистры (от LSB к MSB): • R30 для char (символьного) и unsigned char (беззнакового символьного); • R30, R31 для int (целого) и unsigned int (беззнакового целого); • R30, R31, R22, R23 для long (целого) и unsigned long (беззнакового целого). Таким образом, функция, приведённая в примере, должна возвращать свой результат в регистры R30 и R31. После возврата из функции компилятор автоматически сгенерирует код, ко- торый восстановит пространство Стека данных, использованное параметрами функции. Директива компилятора #pragma warn- запрещает компилятору генерировать предупреждения о том, что функция не возвращает значение. Это нужно, пос- кольку компилятор не знает, что это делается в ассемблерной части функции. Директива компилятора #pragma warn+ разрешит компилятору снова генери- ровать предупреждения. 4.20. Создание библиотек Для того чтобы создать собственные библиотеки пользователя, необходимо последовательно выполнить следующие действия: 1) Создать заголовочный файл с расширением .h.
4.20. Создание библиотек 251 Для этого следует создать новый файл (см. Команда File -» New (Файл -» Но- вый)) и сохранить его под новым именем, например libjiser.h, в поддиректории ..\INC (см. Команда File Save As... (Файл -» Сохранить как...)), В открывшемся окне редактора для этого файла следует набрать текст про- граммы для прототипа функции пользователя. Пример: /* Заголовочный файл для библиотечной функции пользователя */ #pragma used+ // Запретим компилятору генерировать предупреждение о // том, что функция объявлена, но в программе II не используется /* прототип библиотечной функции пользователя */ int summa(int х, int у, int z) ; ttpragma used- // Снова разрешим компилятору генерировать предупреждения tfpragma library lib_user.lib // Эта директива скажет компилятору // компилировать/подсоединять функции из // библиотеки lib_user.lib После набора текста файл следует сохранить (см. Команда File Save (Файл -» Сохранить)). 2) Создать библиотечный файл. Для этого следует создать новый файл (см. Команда File -» New (Файл -» Но- вый)) и сохранить его под именем, например lib_user.c, в любой директории (см. Команда File -» Save As... (Файл -> Сохранить как...)), В открывшемся окне редактора для этого файла следует набрать текст про- граммы для определения функции пользователя. Пример: /* Библиотечный файл для функции пользователя */ int summa(int х, int у, int z) { return x+y+z; } После набора текста файл следует сохранить (см. Команда File -» Save (Файл -» Сохранить)). 3) Конвертировать созданный файл в библиотеку. Для того чтобы конвертировать открытый в настоящий момент файл (lib_user.c) в библиотеку и сохранить его под именем libjiser.lib в директории ..\ЫВ, следует использовать соответствующую команду (см. Команда File -» Convert to Library (Файл -> Преобразовать в библиотеку)). После вызова этой команды появится диалоговое окно New Library Name (Имя новой библиотеки) (Рис. 4.7).
252 Глава 4. Основы языка Си Рис. 4.7. Диалоговое окно New Library Name (Имя новой библиотеки). В этом окне следует ввести имя новой библиотеки (если предложенное имя не устраивает) и щёлкнуть по кнопке ОК. В директории ..\LIB будет создана новая библиотечная функция (Рис. 4.8). Рис. 4.8. Созданная библиотека пользователя lib_user.Iib в директории ..\ЫВ. Для того чтобы использовать вновь созданную библиотеку lib_user.lib, доста- точно в начале программы подключить с помощью директивы #include заголовоч- ный файл lib_user.h. Пример: #include <lib_user.h> // подключение библиотеки пользователя lib_user.lib Библиотечные файлы обычно находятся в директории ..\ЫВ, но можно по- местить их в другие директории и добавить пути к этим директориям при конфи- гурировании проекта (см. Закладка С Compiler (Компилятор Си)).
4.21. Рекомендации 253 4.21. Рекомендации Для уменьшения размера скомпилированного кода и увеличения скорости выполнения программы рекомендуется соблюдать следующие правила: • по возможности использовать unsigned (беззнаковые) переменные; • по возможности использовать минимальный тип данных, т. е. bit (бит) и unsigned char (беззнаковый символьный); • размер битовых переменных, используемых в программе, задаваемый в оп- ции Bit Variables Size (Объём битовых переменных) при конфигурировании проекта (см. Закладка С Compiler (Компилятор Си)), рекомендуется выби- рать как можно меньше, чтобы освободить регистры для распределения других глобальных переменных; • по возможности использовать модель памяти TINY (см. Закладка С Compiler (Компилятор Си)); • всегда сохранять constant strings (строковые константы) во FLASH-памяти, используя ключевое слово flash; • после завершения отладки программы рекомендуется скомпилировать её заново с отключённой опцией Stack End Markers (Отмечать конец стека) (см. Закладка С Compiler (Компилятор Си)). 4.22. Ограничения Эта версия компилятора Си CodeVisionAVR имеет следующие ограничения: • пока не поддерживаются указатели на указатели; • массивы структур или объединений могут иметь только одно измерение; • версия EVALUATION (ПРОБНАЯ) имеет ограничения на размер скомпи- лированного кода; • в версии EVALUATION (ПРОБНАЯ) недоступны функции для LCD 4x40 символов, Philips PCF8563, Philips PCF8583, Dallas Semiconductor DS1302, DS1307.
ГЛАВА ИСПОЛЬЗОВАНИЕ БИБЛИОТЕЧНЫХ ФУНКЦИЙ CodeVisionAVR поставляется с набором библиотечных функций. Одна часть библиотек является встроенной в компилятор, другая (файлы с расширением .lib) располагается в поддиректории ..\ЫВ. Каждая библиотека представляет собой набор определённых функций. На- звания библиотечных функций и соответствующие им заголовочные и библио- течные файлы приведены в Табл. 5.1. Таблица 5.1. Библиотечные функции Функции Заголовочный файл Библиотека Раздел Общие функции Функции символьного типа ctype.h встроенная 5.1 Стандартные функции ввода/вывода языка Си stdio.h Stdio.lib 5.2 Стандартные библиотечные функции stdlib.h Stdlib.lib 5.3 Математические функции math.h Math.lib 5.4 Строковые функции string.h String.lib 5.5 Макросы списков аргументов переменной длины stdarg.h встроенная 5.6 Функции нелокальных переходов setjmp.h Setjmp.lib 5.7 Функции двоично-десятичного преобразования (BCD) bcd.h встроенная 5.8 Функции преобразования кода Грея gray.h Gray.lib 5.9 Функции доступа к памяти mem.h встроенная 5.10 Функции протокола 1-Wire 5.11 Функции температурного датчика DS1820/DS18S20 от Dallas Semiconductor ds1820. h Dsl820.1ib 5.11.1 Функции EEPROM DS2430 от Dallas Semiconductor ds2430.h Ds2430.1ib 5.11.2 Функции EEPROM DS2433 от Dallas Semiconductor ds2433.h Ds2433.1ib 5.11.3 Функции SPI spi.h встроенная 5.12 Функции шины I2C 5.13 Функции температурного датчика LM75 от National Semiconductor lm75.h Lm75.1ib 5.13.1 Функции термометра/термостата DS1621 от Dallas Semiconductor dsl621.h Dsl621.1ib 5.13.2
5.1. Функции символьного типа 255 (продолжение) Функции Заголовочный файл Библиотека Раздел Функции часов реального времени PCF8563 от Philips pcf8563.h Pcf8563.1ib 5.13.3 Функции часов реального времени PCF8583 от Philips pcf8583.h Pcf8583.1ib 5.13.4 Функции часов реального времени DS1307 от Dallas Semiconductor dsl307.h Dsl307.1ib 5.13.5 Функции часов реального времени DS1302 от Dallas Semiconductor dsl302.h Ds 1302.lib 5.14 LCD-функции 5.15 LCD-функции для дисплеев с 2x40 символами Icd.h Led. lib 5.15.1 LCD-функции для дисплеев с 4x40 символами lcd4x40.h Lcd4x40.1ib 5.15.2 LCD-функции для дисплеев, подключённых в режиме отображения 8-битовой памяти Icdstk.h Lcdstk.lib 5.15.3 Функции управления питанием sleep, h встроенная 5.16 Функции задержки delay.h встроенная 5.17 Для использования в программе библиотечных функций необходимо под- ключить с помощью директивы #include соответствующие заголовочные файлы для этих функций. 5.1. Функции символьного типа Перечень функций символьного типа и их действия приведены в Табл. 5.2. Таблица 5.2. Функции символьного типа Функция Действие unsigned char isalnum(char c) Возвращает 1, если символ с — цифра или буква unsigned char isalpha(char c) Возвращает 1, если символ с — буква unsigned char isascii(char c) Возвращает 1, если с — символ ASCII (0...127) unsigned char iscntrl(char c) Возвращает 1, если с — управляющий символ (0...31 или 127) unsigned char isdigit(char c) Возвращает 1, если с — десятичная цифра unsigned char islower(char c) Возвращает 1, если с — буквенный символ нижнего регистра unsigned char isprint(char c) Возвращает 1, если с — печатаемый символ (32... 127) unsigned char ispunct(char c) Возвращает 1, если с — символ пунктуации (все не управляющие и не буквенные) unsigned char isspace(char c) Возвращает 1, если с — символ интервала (пробел, CR, НТ) unsigned char isupper(char c) Возвращает 1, если с — буквенный символ верхнего регистра unsigned char isxdigit(char c) Возвращает 1, если с — шестнадцатеричная цифра char toascii(char c) Возвращает ASCII эквивалент символа с unsigned char toint(char c) Интерпретирует с как шестнадцатеричную цифру и возвращает беззнаковый символ от 0 до 15 char tolower(char c) Возвращает нижний регистр с, если с — символ верхнего регистра, иначе возвращаете char toupper(char c) Возвращает верхний регистр с, если с — символ нижнего регистра, иначе возвращаете
256 Глава 5. Использование библиотечных функций Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций размещаются в файле ctype.h, расположенном в поддиректории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл ctype.h. Пример: /* Использование функций символьного типа */ /* Функция isalnum */ tfinclude <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед использованием функций символьного типа подключим заголовочный файл ctype.h */ ttinclude <ctype.h> /* Основная функция программы */ void main(void) { unsigned char a,b; // объявляем символьные без знака а и b b='R'; // инициализируем переменную Ь /* Вызовем функцию isalnum с фактическим параметром */ a=isalnum(Ь); // а=1, т. к. фактический параметр Ь - буква 5.2. Стандартные функции ввода/вывода языка Си В CodeVisionAVR стандартные функции ввода/вывода языка Си были адапти- рованы для работы на встраиваемых микроконтроллерах, ресурсы которых огра- ниченны. Перечень стандартных функций ввода/вывода языка Си и их действия приве- дены в Табл. 5.3. Таблица 5.3. Стандартные функции ввода/вывода языка Си Функция Действие char getchar(void) Возвращает символ, полученный UART, используя опрос void putchar(char с) Передаёт символ с через UART, используя опрос void puts(char *str) Выводит строку символов str, используя функцию putchar, завершающуюся нулевым символом, расположенную в SRAM, сопровождая вывод символом новой строки void putsf(char flash *str) Выводит строку символов str, расположенную во FLASH, используя функцию putchar, завершающуюся нулевым символом, сопровождая вывод символом новой строки void printf (char flash *fmtstr [, argl, argl,...]) Выполняет форматирование текста, используя функцию putchar, согласно описателям формата в строке fmtstr void sprintf (char *str, char flash *fmtstr [, argl, argl,...]) Эта функция идентична printf, кроме того, что форматированный текст помещается в завершающуюся нулевым символом символьную строку str
5.2. Стандартные функции ввода/вывода языка Си 257 (продолжение) Функция Действие void vprintf (char flash *fintstr, va_list argptr) Эта функция идентична printf, кроме того, что указатель argptr типа va_list указывает на список переменных аргументов. Тип va_list определён в заголовочном файле stdarg.h void vsprintf (char *str, char flash *fmtstr, vajist argptr). Эта функция идентична sprintf, кроме того, что указатель argptr типа vajist указывает на список переменных аргументов. Тип vajist определён в заголовочном файле stdarg.h char *gets (char *str, unsigned char len) Вводит символьную строку str, завершающуюся символом новой строки, используя getchar. Символ новый строки будет заменён нулевым символом. Максимальная длина строки — len. Если было прочитано len символов, а символ новой строки не встретился, тогда строка завершается нулевым символом и функция заканчивается. Функция возвращает указатель в str signed char scanf (char flash *fmtstr [, argl address, argl address,...]) Вводит форматированный текст сканированием, используя getchar, последовательность вводимых полей согласно описателям формата в строке fintstr. Функция возвращает количество успешно введённых данных или —1 при ошибке signed char sscanf (char *str, char flash *frntstr [, argl address, argl address,...]) Эта функция идентична scanf, кроме того, что форматированный текст вводится из завершающейся нулевым символом строки символов str, расположенной в SRAM Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций размещаются в файле stdio.h, расположенном в поддиректории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл stdio.h. Функции ввода/вывода самого низкого уровня — char getchar(void) и void putchar(char с). До использования этих функций необходимо: • задать значение тактовой частоты микроконтроллера; • задать скорость передачи UART; • разрешить передатчик UART; • разрешить приёмник UART. Пример: /* Использование стандартных функций ввода/вывода getchar и putchar */ ttinclude <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед использованием стандартных функций ввода/вывода подключим заголовочный файл stdio.h */ ttinclude <stdio.h> /* Тактовая частота микроконтроллера в Гц */ #define clock 4000000L /* Скорость передачи UART */ tfdefine baud_rate 9600 /* Основная функция программы */
258 Глава 5. Использование библиотечных функций void main(void) { char symbol; // объявляем символьную переменную symbol /* установим скорость передачи UART */ UBRR=clock/16/baud_rate-l ; /* инициализируем регистр управления UART: разрешим приёмник и передатчик, запретим прерывания, данные - 8 бит */ UCR=0xl8; /* Бесконечный цикл */ while (1) { symbol=getchar(); // получим символ, используя // функцию getchar putchar(symbol); // вернём этот же символ обратно, // используя функцию putchar } } Если для ввода/вывода будут использоваться другие периферийные устрой- ства, то потребуется изменить соответственно функции getchar и putchar. Исход- ный код для этих функций находится в файле stdio.h. Все функции ввода/вывода высокого уровня используют функции getchar и putchar. Реализация функций printf и scanf в CodeVisionAVR являются сокращёнными версиями стандартных функций Си. Это было необходимо из-за специфических потребностей микроконтроллера и поскольку полная реализация функций printf и scanf потребовала бы больше пространства FLASH-памяти. Строка описателя формата ftntstr для функций ввода/вывода является конс- тантой и должна быть расположена во FLASH-памяти. Строка описателя формата ftntstr для функций вывода имеет следующую структуру: %[flags][width][.precision][l]type_char Возможные опциональные символы flags (флаги) приведены в Табл. 5.4. Таблица 5.4. Опциональные символы flags Флаг Название Действие I « Минус Если стоит этот флаг, то результат выравнивается по левому краю, а правая часть заполняется пробелами. Если этот флаг отсутствует, то результат будет выровнен по правому краю, а левая часть будет заполнена нулями или пробелами Плюс Если стоит этот флаг, то результаты преобразования со знаком всегда будут начинаться со знака'+' или ' • • Пробел Если стоит этот флаг, то если значение не отрицательное, результат преобразования начнется с пробела. Если значение отрицательное, тогда оно начнется со знака'
5.2. Стандартные функции ввода/вывода языка Си 259 Опциональный описатель width (ширина) устанавливает минимальную шири- ну выходного значения. Если результат преобразования шире, чем ширина поля, оно будет соответственно расширено, так что усечения результата не произойдёт. Поддерживаемые описатели width приведены в Табл. 5.5. Таблица 5.5. Поддерживаемые описатели width Описатель Действие п Будет выведено, по крайней мере, п символов. Если результат имеет менее чем п символов, то пустые поля будут заполнены пробелами. Если использован флаг поля результата будут заполнены справа, в противном случае они заполнятся слева On Будет выведено, по крайней мере, п символов. Если результат имеет менее чем п символов, то слева он заполнится нулями Опциональный описатель precision (точность) устанавливает максимальное количество символов или минимальное количество целых цифр, которые могут быть выведены. Для символов типа преобразования 'е', 'Е' и Т описатель precision устанавлива- ет количество цифр, которые будут выведены справа от десятичной точки. Описатель precision всегда начинается с символачтобы отделить его от опи- сателя width. Поддерживаемые описатели precision приведены в Табл. 5.6. Таблица 5.6. Поддерживаемые описатели precision Описатель Действие отсутствует Точность устанавливается в 1 для символов типа преобразования 'i', 'd', 'u', 'х', 'X'. Для символов типа преобразования 's' и 'р' символьная строка будет выводиться вплоть до первого нулевого символа .0 Точность устанавливается в 1 для символов типа преобразования 'i', 'd', 'u', 'х', 'X' .п п символов или п десятичных порядков Для символов типа преобразования 'i', 'd', 'u', 'х', 'X', если значение имеет менее чем п цифр, то оно заполняется слева нулями. Если у него более чем п цифр, то оно не будет усечено. Для символов типа преобразования 's' и 'р' из символьной строки будет выведено не более чем п символов Для символов типа преобразования 'е', 'Е' и Т будут выведены п цифр справа от знака десятичной дроби. Описатель precision не имеет эффекта на 'с' типе преобразования символов Опциональный модификатор длины Т определяет, что функциональный ар- гумент должен рассматриваться как long int (длинное целое) для символов типа преобразования 'i', 'd', 'u', 'х', 'X', а не как int (целое). Символ типа преобразования type_char используется, чтобы определить путь, по которому следует обращаться к аргументу функции. Поддерживаемые символы типа преобразования приведены в Табл. 5.7.
260 Глава 5. Использование библиотечных функций Таблица 5.7. Поддерживаемые символы типа преобразования для функций вывода Символ Описание Т Аргументом функции является signed decimal integer (десятичное целое со знаком) ’d’ Аргументом функции является signed decimal integer (десятичное целое со знаком) ‘и’ Аргументом функции является unsigned decimal integer (десятичное целое без знака) ’е’ Аргументом функции является float (вещественное с плавающей точкой), который будет выводиться, используя формат [-]d.ddddde[-]dd Е’ Аргументом функции является float (вещественное с плавающей точкой), который будет выводиться, используя формат [-Jd.dddddE[-]dd Т Аргументом функции является float (вещественное с плавающей точкой), который будет выводиться, используя формат [-]ddd.ddddd ’х’ Аргументом функции является unsigned hexadecimal integer (шестнадцатеричное целое без знака), который будет выводиться символами нижнего регистра X’ Аргументом функции является unsigned hexadecimal integer (шестнадцатеричное целое без знака), который будет выводиться символами верхнего регистра ’с’ Аргументом функции является single character (символ со знаком) ’s’ Аргументом функции является указатель на завершающуюся нулем строку символов, расположенную в SRAM ’р’ Аргументом функции является указатель на завершающуюся нулем строку символов, расположенную во FLASH ’%’ Будет выведено'%' символов Для того чтобы уменьшить размер программного кода, при конфигурирова- нии проекта (см. Закладка С Compiler (Компилятор Си)) можно задать характерис- тики функций printf и sprintf (опция (s)printf features ((s)printf характеристики)). Это позволяет связать разные версии функций printf и sprintf только с теми харак- теристиками, которые действительно требуются программе. Чем больше характеристик выбрано для функцийprintf и sprintf, тем боль- ше размер сгенерированного кода. Строка описателя формата ftntstr для функций ввода имеет следующую струк- туру: %[width][l]type_char Опциональный описатель width (ширина) устанавливает максимальное коли- чество символов для чтения. Если функция сталкивается с символом пробела или с символом, который не может быть сконвертирован, то она продолжит ввод сле- дующего поля, если оно есть. Опциональный модификатор длины ввода Т определяет, что функциональ- ный аргумент должен рассматриваться как long int (длинное целое) для символов типа преобразования Т, *d’, ’и’, ’х*, а не как int (целое). Символ типа преобразования type_char используется, чтобы определить спо- соб, которым будет обработано вводимое поле. Поддерживаемые символы типа преобразования приведены в Табл. 5.8.
5.2 Стандартные функции ввода/вывода языка Си 261 Таблица 5.8. Поддерживаемые символы типа преобразования для функций ввода Символ Описание Т Вводит signed decimal integer (десятичное целое со знаком) в указатель на int (целый) аргумент ’d' Вводит signed decimal integer (десятичное целое со знакомив указатель на int (целый) аргумент 'и' Вводит unsigned decimal integer (десятичное целое без знака) в указатель на unsigned int (целый без знака) аргумент ’х’ Вводит unsigned hexadecimal integer (шестнадцатеричное целое без знака) в указатель на unsigned int (целый без знака) аргумент ’с’ Вводит ASCII char (символ ASCII) в указатель на char (символьный) аргумент ’s’ Вводит ASCII character string (символьную строку ASCII) в указатель на char (символьный) аргумент Ввода не происходит, сохраняется '%' Для того чтобы уменьшить размер программного кода, при конфигурирова- нии проекта (см. Закладка С Compiler (Компилятор Си)) можно задать характерис- тики функций scanf и sscanf (опция (s)scanf features ((s)scanfхарактеристики)). Это позволяет связать разные версии функций scanf и sscanf только с теми характе- ристиками, которые действительно требуются программе. Чем больше характеристик выбрано для функций scanf и sscanf тем боль- ше размер сгенерированного кода. Пример: /* Использование стандартной функции ввода/вывода printf */ #include <90s8515.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед использованием стандартных функций ввода/вывода подключим заголовочный файл stdio.h */ ttinclude <stdio.h> /* Основная функция программы */ void main(void) { int x=83; // объявляем и инициализируем десятичным // значением целую переменную х int у=0хА0; // объявляем и инициализируем шестнадцатеричным // значением целую переменную у float z=12.345678; // объявляем и инициализируем переменную // с плавающей точкой z /* инициализируем UART AT90s8515: разрешим передатчик, запретим прерывания, данные — 8 бит */ UCR=0x8; /* Скорость передачи Baud=9600 при тактовой частоте 4 МГц */
262 Глава 5. Использование библиотечных функций UBRR=0xl9; /* Функция printf является универсальной функцией форматных преобразований. Каждый знак % указывает, куда должен подставляться каждый из аргументов и в какой форме он должен выводиться. Количество % должно совпадать с количеством аргументов */ printf("%-5d ", х); /* спецификация преобразования %-5d означает, что число х будет напечатано в поле шириной, по крайней мере, в пять символов (описатель width = 5)в десятичном виде (символ преобразования 'd'). Если х имеет менее 5 цифр, то оно будет выровнено по левому краю (флаг '-'), а правая часть будет заполнена пробелами (т. к. в описателе width в начале отсутствует 0). Таким образом, будет выведено: "83 " (три пробела) */ printf("%06d\n",х); /* спецификация преобразования %06d\n означает, что число х будет напечатано в поле шириной, по крайней мере, в шесть символов (описатель width = 0б)в десятичном виде(символ преобразования^'). Последовательность \п является обозначением 'символа новой строки', который служит указанием для перехода на терминале к левому краю следующей строки. Так как в предыдущем операторе отсутствует \п, то вывод будет осуществлён в той же строке, что и предыдущее значение. Если х имеет менее б цифр, то оно будет выровнено по правому краю (т. к. флаг отсутствует), а левая часть будет заполнена нулями (т. к. в описателе width в начале стоит 0). Таким образом, будет выведено: "000083" */ printf("%i %i\n",x,y); /* спецификация преобразования %i %i\n означает, что сначала будет выведено число х (флаги и описатель width отсутствуют) в десятичном виде (символ преобразования 'i' ) . Затем будет выведен пробел, а затем число у (флаги и описатель width отсутствуют) в десятичном виде (символ преобразования 'i'). После этого будет осуществлён переход на новую строку (последовательность \п). Таким образом, будет выведено: "83 160", т. к. у=0хА0=1б0 */ printf("%d=0x%x \t",y,y); /* спецификация преобразования %d=0x%x \t означает, что сначала будет выведено число у (флаги и описатель width отсутствуют) в десятичном виде (символ преобразования 'd'). Затем будет выведена последовательность "=0х", а затем число у (флаги и описатель width отсутствуют) в шестнадцатеричном виде символами нижнего регистра (символ преобразования 'х'). После этого будет выведен пробел и осуществлена табуляция, т. е. вывод еще нескольких пробелов (последовательность \t). Таким образом, будет выведено: "1б0=0ха0 " */ printf("%d=0x%X\n",у,у); /* спецификация преобразования %d=0x%X\n означает, что сначала будет
5.2. Стандартные функции ввода/вывода языка Си 263 выведено число у (флаги и описатель width отсутствуют) в десятичном виде (символ преобразования ’d’). Затем будет выведена последовательность "=0х", а затем число у (флаги и описатель width отсутствуют) в шестнадцатеричном виде символами верхнего регистра (символ преобразования 'X'). Так как в предыдущем операторе отсутствует \п, то вывод будет осуществлён в той же строке, что и предыдущее значение. После этого будет осуществлён переход на новую строку (последовательность \п). Таким образом, будет выведено: "1б0=0хА0" */ printf("%i=0x%x\n",х,х); /* спецификация преобразования %i=0x%x\n означает, что сначала будет выведено число х (флаги и описатель width отсутствуют) в десятичном виде (символ преобразования ' i') . Затем будет выведена последовательность "=0х", а затем число х (флаги и описатель width отсутствуют) в шестнадцатеричном виде символами нижнего регистра (символ преобразования 1х’). После этого будет осуществлён переход на новую строку (последовательность \п). Таким образом, будет выведено: "83=0x53", т. к. х=83=0х53 */ printf("%с\п",х); /* спецификация преобразования %с\п означает, что будет выведено число х в виде символа ASCII (символ преобразования 'с'). После этого будет осуществлён переход на новую строку (последовательность \п) . Таким образом, будет выведено "S", т. к. десятичный код 83 соответствует символу 'S' ASCII */ printf("%е %.1е %2.2e\n",z,z,z); /* спецификация преобразования %е %.1е %2.2е\п означает, что сначала будет выведено число z (флаги и описатели width и precision отсутствуют) с 5 знаками (по умолчанию описатель precision = 5) после запятой в виде [-]d.ddddd е[-]dd (символ преобразования 'е'), причём будет осуществлено округление числа, т. к. в числе z после запятой б знаков (флаги и описатели width и precision отсутствуют). Затем будет выведено число z с 1 знаком (описатель precision 1) после запятой в виде [-]d.d e[-]dd (символ преобразования 'е'), причём будет осуществлено округление числа. После этого будет выведено число z с 2 знаками после запятой (описатель precision 2) в виде [-]d.dd e[-]dd (символ преобразования 'е'), причём будет осуществлено округление числа. Описатель width = 2 здесь не имеет значения, он может быть любым или отсутствовать. После этого будет осуществлён переход на новую строку (последовательность \п). Таким образом, будет выведено: "1.23457е1 1.2е1 1.23е" */ } При моделировании данного примера в AVR Studio версии 3 в окне Terminal I/O (Терминала ввода/вывода) после выполнения программы можно увидеть все значения в соответствующем формате (Рис. 5.1).
264 Глава 5. Использование библиотечных функций Рис. 5.1. Результаты использования функции printf. Следует отметить, что последовательность \п представляет только один сим- вол. Условные «последовательности», подобные \п, дают общий и допускающий расширение механизм для представления трудных для вывода или невидимых символов. Среди прочих символов в языке Си предусмотрены следующие: \t — для табуляции, \Ь — для возврата на одну позицию, \» — Для двойной кавычки, \г — для возврата каретки, \\ — для самой обратной косой черты, \xab — для вы- вода символа с шестнадцатеричным кодом ab и \0abc — для вывода символа с восьмиричным кодом abc. Пример: /* Использование стандартных функций ввода/вывода sprintf и puts */ ttinclude <90s8515.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед использованием стандартных функций ввода/вывода подключим заголовочный файл stdio.h */ #include <stdio.h> char string_out[30]; // объявляем строковую переменную (символьный // массив) string_out, которая может содержать // до 30 символов /* Основная функция программы */ void main(void) { int x=83; // объявляем и инициализируем десятичным // значением целую переменную х int y=-lll; // объявляем и инициализируем шестнадцатеричным // значением целую переменную у float z=12.3; // объявляем и инициализируем переменную И с плавающей точкой z /* Осуществим форматный вывод значений переменных х, у, zb строковую
5.2. Стандартные функции ввода/вывода языка Си 265 переменную string_out */ /* Функция sprintf осуществляет форматные преобразования аналогично функции printf, но помещает результаты в строку, а не в стандартный вывод. Конечно, строка должна быть достаточно велика, чтобы принять результат. Точно так же каждый знак % указывает, куда должен подставляться каждый из аргументов и в какой форме он должен выводиться. Количество % должно совпадать с количеством аргументов */ sprintf(string_out,"x=%-i\ny=%-d\nz=%-.3e\n",х,у,z); /* спецификация преобразования x=%-i\ny=%-d\nz=%-.Зе\п означает, что сначала будет выведена последовательность "х=", затем число х в десятичном виде (символ преобразования ' i ' ) , причём результат будет выровнен по левому краю (флаг Описатель width отсутствует. После этого будет выведен символ перевода строки (последовательность \п). Затем будет выведена последовательность "у=", затем число у в десятичном виде (символ преобразования 'd'), причём результат будет выровнен по левому краю (флаг Описатель width отсутствует. После этого будет выведен символ перевода строки (последовательность \п). После этого будет выведена последовательность "z=", затем число z с 3 знаками (описатель precision 3) после запятой (в виде [-]d.ddd e[-]dd (символ преобразования ’е’)), причём результат будет выровнен по левому краю (флаг После этого будет выведен символ перевода строки (последовательность \п). Таким образом, в строку string_out будет выведено: "x=83\ny=-lll\nz=-l.230е1" */ /* С помощью UART осуществим вывод строки string_out, используя стандартную функцию ввода/вывода puts */ /* Инициализируем UART AT90s8515: разрешим передатчик, запретим прерывания, данные - 8 бит */ UCR=0x8; /* Скорость передачи Baud=9600 при тактовой частоте 4 МГц */ UBRR=25; puts(string_out); // выводим содержимое строки string_out /* Таким образом, будет выведено: "х=83 у=-111 z=-1.230el" */ }
266 Глава 5. Использование библиотечных функций При моделировании данного примера в AVR Studio версии 3 в окне Terminal I/O (Терминала ввода/вывода) после выполнения программы можно увидеть все значения в соответствующем формате (Рис. 5.2). Рис. 5.2. Результаты использования функций sprintf и puts. Перед моделированием этих примеров в AVR Studio версии 3 при конфигури- ровании проектов на закладке С Compiler (Компилятор Си) (см. Закладка С Compiler (Компилятор Си)) в опции (s)printf features ((s)printf характеристики) сле- дует выбрать float, width, precision, т. к. в данных примерах используются перемен- ные типа float (с плавающей точкой). В опции File Output Format(s) (Формат(ы) выходных файлов) следует выбрать набор файлов с расширением COF, а также выбрать опцию Use the Terminal I/O in AVR Studio (Использовать терминал вво- да/вывода в AVR Studio). Только после этого проекты можно строить (см. Коман- да Project -> Make (Проект -> Построить)). 5.3. Стандартные библиотечные функции Перечень стандартных библиотечных функций и их действия приведены в Табл. 5.9. Таблица 5.9. Стандартные библиотечные функции Функция Действие int atoi(char *str) Преобразует символьную строку str в integer (целое) long int atol(char *str) Преобразует символьную строку str в long integer (длинное целое) void itoa(int n, char *str) Преобразует integer (целое) п в символы в строке str void ltoa(Iong int n, char *str) Преобразует long integer (длинное целое) п в символы в строке str void ftoa (float n, unsigned char decimals, char *str) Преобразует floating point (число с плавающей точкой) п в символы в строке str. Число представляется с определённым количеством цифр после точки void ftoe (float n, unsigned char decimals, char *str) Преобразует floating point (число с плавающей точкой) п в символ в строке str. Число представляется, как мантисса, с определённым количеством цифр после точки, и целый десятичный показатель степени (например, 65.43е-2) float atof(char *str) Преобразует символы из строки str в floating point (число с плавающей точкой)
5.3. Стандартные библиотечные функции 267 (продолжение) Функция Действие int rand (void) Генерирует псевдослучайное число между 0 и 32767 void srand(int seed) Устанавливает начальное значение seed, используемое генератором псевдослучайных чисел в функции rand Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций размещаются в файле stdlib.h, расположенном в поддиректории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл stdlib.h. Пример: /* Использование стандартных библиотечных функций ★/ /★ Функция atoi */ Hnclude <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед использованием стандартных библиотечных функций подключим заголовочный файл stdlib.h */ #include <stdlib.h> /★ Основная функция программы */ void main(void) { int с; // объявляем целую переменную с char str[]="1234"; // объявляем и инициализируем строковую // переменную (символьный массив) str char *point; // объявляем указатель point c=atoi(str); // В результате с=1234 /* То же с использованием указателя */ point=&str[0]; // Присваиваем указателю адрес самого // первого символа строки str c=atoi(point); // В результате с=1234 point=&str[2]; // Если присвоить указателю адрес третьего // символа строки str, то c=atoi(point); // в результате с=34 } Пример: /* Использование стандартных библиотечных функций */ /* Функция itoa*/ Hnclude <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR
268 Глава 5. Использование библиотечных функций /★ Перед использованием стандартных библиотечных функций подключим заголовочный файл stdlib.h */ #include <stdlib.h> /* Основная функция программы */ void main(void) int с; // объявляем целую переменную с char str[4]; И объявляем строковую переменную str char *point; // объявляем указатель point c=265; // инициализируем переменную с itoa(c, str); и В результате str[4]="265" /* То же с использованием указателя */ point=&str[0]; // Присваиваем указателю адрес самого // первого символа строки str itoa(c, point); // В результате str[4]="265" } Пример: /* Использование стандартных библиотечных функций */ /* Функции rand и srand */ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед использованием стандартных библиотечных функций подключим заголовочный файл stdlib.h */ #include <stdlib.h> /* Основная функция программы */ void main(void) { int initial,amount,random,i; // Объявляем целые переменные /★ Задаем начальное значение для функции rand, генерирующей псевдослучайные числа */ initial=3; srand(initial); /* Задаем количество генераций псевдослучайных чисел */ amount=10; for (i=l; i<=amount; i++) // Оператор цикла { random=rand(); // random - псевдослучайное число } }
5.4. Математические функции 269 5.4. Математические функции Перечень математических функций и их действия приведены в Табл. 5.10. Таблица 5.10. Математические функции Функция Действие unsigned char cabs(signed char x) Возвращает абсолютное значение байта х unsigned int abs(int x) Возвращает абсолютное значение integer (целого) х unsigned long labs(long intx) Возвращает абсолютное значение long integer (длинного целого) х float fabs(float x) Возвращает абсолютное значение floating point (числа с плавающей точкой)X signed char стах (signed char a, signed char b) Возвращает максимальное значение байтов а и b int max(int a, int b) Возвращает максимальное значение integers (целых) а и b long int hnax(long int a, long int b) Возвращает максимальное значение long integers (длинных целых) а и b float fmax(float a, float b) Возвращает максимальное значение floating point (чисел с плавающей точкой) а и b signed char cmin (signed char a, signed char b) Возвращает минимальное значение байтов а и b int min(int a, int b) Возвращает минимальное значение integers (целых) а и b long int lmin(long int a, long int b) Возвращает минимальное значение long integers (длинных целых) а и b float fmin(float a, float b) Возвращает минимальное значение floating point (чисел с плавающей точкой) а и b signed char csign(signed char x) Возвращает -1,0 или 1, если байт х — отрицательный, нулевой или положительный signed char sign(int x) Возвращает -1,0 или 1, если integer (целое) х — отрицательное, нулевое или положительное signed char lsign(long int x) Возвращает -1,0 или 1, если long integer (длинное целое) х — отрицательное, нулевое или положительное signed char fsign(float x) Возвращает -1,0 или 1, если floating point (число с плавающей точкой) х — отрицательное, нулевое или положительное unsigned char isqrt(unsigned int x) Возвращает квадратный корень unsigned integer (целого без знака) х unsigned int lsqrt(unsigned long x) ‘ Возвращает квадратный корень unsigned long integer (длинного целого без знака) х float sqrt(float x) Возвращает квадратный корень положительного floating point (числа с плавающей точкой) х float floor(float x) Возвращает ближайшее меньшее целое значение floating point (числа с плавающей точкой) х float ceil(float x) Возвращает ближайшее большее целое значение floating point (числа с плавающей точкой) х
270 Глава 5. Использование библиотечных функций (продолжение) Функция Действие float fmod(float х, float у) Возвращает остаток деления x на у float modf(float х, float *ipart) Разделяет floating point (числа с плавающей точкой) х на целую и дробную части. Дробная часть х возвращается как signed floating point (число с плавающей точкой со знаком). Целая часть сохраняется как floating point (число с плавающей точкой) в ipart float ldexp(float x, int expn) Возвращает х * 2ехрп float frexp(float x, int *expn) Возвращает мантиссу и показатель степени floating point (числа с плавающей точкой) х float exp(float x) Возвращает ех float logffloat x) Возвращает натуральный логарифм floating point (числа с плавающей точкой) х float log 10(float x) Возвращает десятичный логарифм floating point (числа с плавающей точкой)X float pow(float x, float y) Возвращает ху float sin(floatx) Возвращает синус floating point (числа с плавающей точкой) х, где угол выражен в радианах float cos(f loat x) Возвращает косинус floating point (числа с плавающей точкой) х, где угол выражен в радианах float tan(float x) Возвращает тангенс floating point (числа с плавающей точкой) х, где угол выражен в радианах float sinh (float x) Возвращает гиперболический синус floating point (числа с плавающей точкой) х, где угол выражен в радианах float cosh(float x) Возвращает гиперболический косинус floating point (числа с плавающей точкой) х, где угол выражен в радианах float tanh(float x) Возвращает гиперболический тангенс floating point (числа с плавающей точкой) х, где угол выражен в радианах float asin(float x) Возвращает арксинус floating point (числа с плавающей точкой) х (в диапазоне -л/2...л/2). х должно быть в диапазоне -1... 1 float acos(f loat x) Возвращает арккосинус floating point (числа с плавающей точкой) х (в диапазоне 0...л). х должно быть в диапазоне -1... 1 float atan(float x) Возвращает арктангенс floating point (числа с плавающей точкой) х (в диапазоне -л/2...л/2) float atan2 (float y, float x) Возвращает арктангенс floating point (числа с плавающей точкой) у/х (в диапазоне -л...л) Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ!
5.4. Математические функции 271 Прототипы этих функций размещаются в файле math.h, расположенном в поддиректории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл math.h. Пример: /* Использование математических функций */ /* Функция sign, которая возвращает -1, 0 или 1, если целое число, соответственно, отрицательное, нулевое или положительное ★/ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед использованием математических функций подключим заголовочный файл math.h */ #include <math.h> /* Основная функция программы */ void main(void) { int a,b,c; // объявляем целые переменные a,b,c signed char znak; // объявляем символьную переменную со // знаком znak a=-10; // инициализируем переменную а b=0; // инициализируем переменную b c=19; // инициализируем переменную с /* Вызываем функцию sign с фактическими параметрами znak =sign(а) , ; // znak=-l, т . к . а<0 znak =sign(b)( ; // znak=0, т. к. Ь=0 znak =sign(с) ; ; II znak=l, т. к. с>0 } Пример: /* Использование математических функций */ /* Функция стах, возвращающая максимальное значение из двух символьных переменных со знаком */ #include <90s8515.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед использованием функций подключим заголовочный файл math.h */ #include <math.h> /* Основная функция программы */ void main(void) { signed char a,b,c; // объявляем символьные co знаком // переменные a,b,c
272 Глава 5. Использование библиотечных функций а=10; И инициализируем переменную а Ь=19; II инициализируем переменную b /* Вызываем функцию стах с фактическими параметрами */ c=cmax(a,b); // с=19, т. к. 19>10 5.5. Строковые функции Перечень строковых функций и их действия приведены в Табл. 5.11. Таблица 5.11. Строковые функции Функция Действие char *strcat(char *strl, char *str2) Присоединяет строку str2 к концу строки str 1 char *strcatf (char *strl, char flash *str2) Присоединяет строку str2, расположенную во FLASH, к концу строки strl char *stmcat (char *strl, char *str2, unsigned char n) Присоединяет максимум п символов строки str2 к концу строки strl. Возвращает указатель на строку strl char *strncatf (char *strl, char flash *str2, unsigned char n) Присоединяет максимум п символов строки str2, расположенной во FLASH, к концу строки strl. Возвращает указатель на строку strl char *strchr(char *str, char c) Возвращает указатель на первый встретившийся символ с в строке str, иначе указатель NULL (НОЛЬ) char *strrchr(char *str, char c) Возвращает указатель на последний встретившийся символ с в строке str, иначе указатель NULL (НОЛЬ) signed char strops (char *str, char c) Возвращает индекс первого встретившегося символа с в строке str, иначе — 1 signed char strrpos (char *str, char c) Возвращает индекс последнего встретившегося символа с в строке str, иначе — 1 signed char strcmp (char *strl, char *str2) Сравнивает строку strl со строкой str2. Возвращает значение <0, 0, >0, если соответственно strl<str2, strl=str2, strl>str2 signed char strcmpf (char *strl, char flash *str2) Сравнивает строку strl, расположенную в SRAM, со строкой str2, расположенной во FLASH. Возвращает значение <0, 0, >0, если соответственно strl<str2, strl=str2, strl>str2 signed char stmcmp (char *strl, char *str2, unsigned char n) Сравнивает не более чем п символов строки strl со строкой str2. Возвращает значение <0, 0, >0, если соответственно str 1 <str2, strl=str2, strl>str2 signed char strncmpf (char *strl, char flash *str2, unsigned char n) Сравнивает не более чем п символов строки strl, расположенной в SRAM, со строкой str2, расположенной во FLASH. Возвращает значение <0, 0, >0, если соответственно strl<str2, strl=str2, strl>str2 char *strcpy(char *dest, char *src) Копирует строку src в строку dest char *strcpyf (char *dest, char flash *src) Копирует строку src, расположенную во FLASH, в строку dest, расположенную в SRAM. Возвращает указатель на строку dest char *stmcpy (char *dest, char *src, unsigned char n) Копирует не более чем п символов из строки src в строку dest (заполнение нулями). Возвращает указатель на строку dest char *strncpyf (char *dest, char flash *src, unsigned char n) Копирует не более чем п символов из строки src, расположенной во FLASH, в строку dest, расположенную в SRAM (заполнение нулями). Возвращает указатель на строку dest
5.5. Строковые функции 273 (продолжение) Функция Действие unsigned char strspn (char *str, char *set) Возвращает индекс первого символа из строки str, который не совпадает с символом из строки set. Если все символы set совпадают с символами str, то возвращает длину str unsigned char strspnf (char *str, char flash *set) Возвращает индекс первого символа из строки str, расположенной в SRAM, который не совпадает с символом из строки set, расположенной во FLASH. Если все символы set совпадают с символами str, то возвращает длину str unsigned char strcspn (char *str, char *set) Ищет в строке str первое появление символа из строки set. Если есть совпадение, возвращает индекс символа в строке str. Если нет совпадающих символов, возвращает длину str unsigned char strcspnf (char *str, char flash *set) Ищет в строке str первое появление символа из строки set, расположенной во FLASH. Если есть совпадение, возвращает индекс символа в строке str. Если нет совпадающих символов, возвращает длину str char *strpbrk(char *str, char *set) Ищет в строке str первое появление символа из строки set. Если есть совпадение, возвращает указатель на символ в строке str. Если нет совпадающих символов, возвращает указатель NULL (НОЛЬ) char *strpbrkf (char *str, char flash *set) Ищет в строке str, расположенной в SRAM, первое появление символа из строки set, расположенной во FLASH. Если есть совпадение, возвращает указатель на символ в строке str. Если нет совпадающих символов, возвращает указатель NULL (НОЛЬ) char *strrpbrk(char *str, char *set) Ищет в строке str появление последнего символа из строки set. Если есть совпадение, возвращает указатель на символ в строке str. Если нет совпадающих символов, возвращает указатель NULL (НОЛЬ) char *strrpbrkf (char *str, char flash *set) Ищет в строке str, расположенной в SRAM, появление последнего символа из строки set, расположенной во FLASH. Если есть совпадение, возвращает указатель на символ в строке str. Если нет совпадающих символов, возвращает указатель NULL (НОЛЬ) char *strstr(char *strl, char *str2) Ищет в строке strl первое появление строки str2. Если есть совпадение, возвращает указатель на символ в строке strl, где начинается str2. Если нет совпадения, возвращает указатель NULL (НОЛЬ) char *strstrf (char *strl, char flash *str2) Ищет в строке strl, расположенной в SRAM, первое появление строки str2, расположенной во FLASH. Если есть совпадение, возвращает указатель на символ в строке strl, где начинается str2. Если нет совпадения, возвращает указатель NULL (НОЛЬ) char *strtok (char *strl, char flash *str2) Сканирует строку strl, расположенную в SRAM, до первого знака, не содержащегося в строке str2, расположенной во FLASH. Функция рассматривает строку strl как состоящую из последовательности текстовых знаков, разделенных промежутками одного или более символов из строки str2. Первый вызов strtok с указателем на строку strl, отличным от NULL (НУЛЯ), возвращает указатель на первый символ первого знака в строке strl. Также сразу после возвращённого знака в строку strl будет записан NULL (НУЛЕВОЙ) символ. Последующие вызовы strtok с первым параметром NULL (ноль) будут работать через строку strl до тех пор, пока знаков больше не останется. Когда знаков больше нет, strtok возвращает указатель NULL (НОЛЬ) unsigned char strlen(char *str)° Возвращает длину строки str (в диапазоне 0...255) unsigned int strlen(char *str)2) Возвращает длину строки str (в диапазоне 0...65535)
274 Глава 5. Использование библиотечных функций (продолжение) Функция Действие unsigned int strienffchar flash *str) Возвращает длину строки str, расположенной во FLASH void *memcpy(void *dest,void *src, unsigned char n)1} Копируют п байтов из строки src в строку dest. dest не должна перекрывать src, иначе следует использовать функцию memmove. Возвращают указатель на dest void *memcpy(void *dest,void *src, unsigned int n)2) void *memcpyf(void *dest, void flash *src, unsigned char n)° Копируют п байтов из строки src, расположенной во FLASH, в строку dest. Возвращают указатель на dest void *memcpyf(void *dest. void flash *src, unsigned int n)2) void *memccpy(void *dest, void *src, char c, unsigned char n)1} Копируют не более чем п байтов из строки src в строку dest, пока не будет скопирован символ с. dest не должна перекрывать src. Возвращают указатель NULL (НОЛЬ), если последний скопированный символ был с или указатель на dest+n+1 void *memccpy(void *dest, void *src, char c, unsigned int n)2) void *memmove(void *dest, void *src, unsigned char n)° Копируют п байтов из src в строку dest. dest может перекрывать src. Возвращают указатель на dest void *memmove(void *dest, void *src, unsigned int n)2) void *memchr(void *buf, unsigned char c, unsigned char n)1’ Сканируют п байтов из строки buf до байта с. Возвращают указатель на с, если он обнаружен, или указатель NULL (НОЛЬ), если не обнаружен void *memchr(void *buf, unsigned char c, unsigned int n)2) signed char memcmp(void *bufl, void *buf2, unsigned char n)1} Сравнивают не более чем п байтов строки bufl с bufl. Возвращают <0, 0, >0, если соответственно bufl<buf2, bufl=buf2, bufl>buf2 signed char memcmp(void *bufl, void *buf2, unsigned int n)2) signed char memcmpffvoid *bufl, void flash *buf2, unsigned char n)° Сравнивают не более чем п байтов строки bufl, расположенной в SRAM, с ЬиП, расположенной во FLASH. Возвращают <0, 0, >0, если соответственно bufl<buf2, bufl=buf2, bufl>buf2 signed char memcmpffvoid *bufl, void flash *buf2, unsigned int n)2) void *memset(void *buf, unsigned char c, unsigned char П)” Устанавливают п байтов в строке buf такими же, как байт с. Возвращают указатель на строку buf void *memset(void *buf, unsigned char c, unsigned int n)2) ° Для модели памяти TINY 2) Для модели памяти SMALL Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций размещаются в файле string.h, расположенном в поддиректории ..\INC, Перед использованием этих функций директивой #include должен быть подключён файл string.h. Функции обработки строки были расширены, чтобы обрабатывать строки, расположенные как в SRAM, так и во FLASH-памяти.
5.6. Макросы списков аргументов переменной длины 275 Пример: /* Использование строковых функций */ /* Функция streatf, которая присоединяет строку, расположенную во FLASH, к концу строки, расположенной в SRAM */ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /★ Перед использованием строковых функций подключим заголовочный файл string.h */ #include <string.h> char stringl[]="Hello // объявляем и инициализируем // строку stringl, расположенную в // SRAM flash char string2[]="World!";// объявляем и инициализируем // строку string2, расположенную во // FLASH /* Основная функция программы */ void main(void) /* Вызовем функцию strcatf с фактическими параметрами */ streatf(stringl, string2); /* Теперь к концу строки stringl присоединена строка string2 и строка stringl стала "Hello World!" */ } 5.6. Макросы списков аргументов переменной длины Перечень макросов списков аргументов переменной длины и их действия приведены в Табл. 5.12. Таблица 5.12, Макросы списков аргументов переменной длины Макрос Действие void va_start(argptr, previous_par) Этот макрос, когда использован в функции со списком аргументов переменной длины, инициализирует указатель argptr типа va_Iist для последующего использования макросами va_arg и va_end. Аргумент previous_par должен быть именем обязательного аргумента функции, непосредственно предшествующего опциональным аргументам. Макрос va_start должен быть вызван до любого доступа, использующего макрос va_arg type va_arg(argptr, type) При первом вызове va_arg возвращает первый аргумент после аргумента previous_par, определённого в макросе va_start. Последующие вызовы va_arg последовательно возвращают остальные аргументы void va_end(argptr) Этот макрос используется, чтобы завершить использование указателя списка аргументов переменной длины argptr, который инициализирован макросом va_start
276 Глава 5. Использование библиотечных функций Обратите внимание, что все макросы должны быть записаны в одну строку. Переносы НЕ ДОПУСКАЮТСЯ! Эти макросы определены в файле stdarg.h, расположенном в поддиректории ..\INC. Перед использованием этих макросов директивой #inchide должен быть подключён файл stdarg.h. Пример: /* Использование макросов */ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед использованием макросов списков аргументов переменной длины подключим заголовочный файл stdarg.h */ ttinclude <stdarg.h> /* Объявим и определим функцию с переменным количеством аргументов, которая возвращает сумму всех аргументов */ /* Обязательный аргумент п в данной функции является количеством опциональных аргументов. Он непосредственно предшествует опциональным аргументам, которые обозначены многоточием. Следовательно, его имя должно быть использовано в макросе va_start в качестве аргумента previous_par */ int summa(int n, ...) II Объявляем и определяем функцию summa { va_list point; // Объявляем переменную point типа va_list, // point - указатель // Тип va_list определён директивой typedef //в файле stdarg.h. int index, sum_int; // Объявляем целые переменные index - индекс //и sum_int - сумма всех аргументов sum_int=0; // инициализируем переменную sum_int /* инициализируем указатель point */ va_start(point,n); // указателю point присваиваем адрес // первого опционального аргумента функции, // следующего за обязательным аргументом п, // используя макрос va_start /* добавим все опциональные аргументы функции, которые идут после п */ for (index=l; index<=n; index++) // цикл for { sum_int=sum_int+va_arg(point, int);// последовательно // прибавляем каждый // аргумент к // переменной sum_int, // чтобы получить сумму // всех аргументов } /* завершим использование указателя point */
5.7. Функции нелокальных переходов 277 va_end(point); // используем макрос va_end return sum_int; // возвращаем сумму всех аргументов } /★ Основная функция программы */ void main(void) { int summa_3arg; // объявляем целую переменную summa_3arg // summa_3arg - сумма 3 аргументов int summa_2arg; // объявляем целую переменную summa_2arg // summa_2arg - сумма 2 аргументов /* вычислим сумму 3 аргументов */ summa_3arg=summa(3,12,2,24); // вызываем функцию summa // Первое число в списке аргументов - // количество опциональных аргументов (3) // Результат: summa_3arg=38 /* вычислим сумму 2 аргументов */ summa_2arg=summa(2,136,15); // вызываем функцию summa // Первое число в списке аргументов - // количество опциональных аргументов (2) // Результат: summa_2arg=151 } 5.7. Функции нелокальных переходов Эти функции могут выполнять нелокальные переходы. Они обычно исполь- зуются для передачи управления в программу исправления ошибки. Перечень функций нелокальных переходов и их действия приведены в Табл. 5.13. Таблица 5.13. Функции нелокальных переходов Функция Действие void longjmp(char *env, int retval) Эта функция восстанавливает состояние CPU, которое прежде было сохранено в переменной env вызовом функции setjmp. Аргумент retval содержит целое ненулевое значение, которое будет возвращено функцией setjmp после вызова longjmp. Если передано значение 0 как аргумент retval, то оно будет заменено на 1 int setjmp(char *env) Эта функция сохраняет текущее состояние CPU (регистры Y, SP, SREG и текущий адрес инструкции) в переменной env. Состояние CPU затем можно восстановить последующим вызовом функции longjmp. После вызова функции setjmp выполнение сразу продолжится. Если текущее состояние CPU сохранено в переменной env, функция setjmp возвращает 0. Если функция возвращает значение, отличное от 0, она сигнализирует о том, что была выполнена функция longjmp. В этой ситуации возвращённое значение — то, которое было передано как аргумент retval в функцию longjmp. Для того чтобы сохранить локальные переменные в функции, где используется setjmp, они должны быть объявлены с атрибутом volatile Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ!
278 Глава 5. Использование библиотечных функций Прототипы этих функций определены в файле setjmp.h, расположенном в поддиректории ,.\INC. Перед использованием этих функций директивой #include должен быть подключён файл setjmp.h. Для того чтобы облегчить использование этих функций, заголовочный файл setjmp.h содержит определение типа данных jmp buf, который используется при объявлении переменных env. Пример: /* Использование функций нелокальных переходов */ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед использованием функций нелокальных переходов подключим заголовочный файл setjmp.h */ #include <setjmp.h> /* объявим переменную, которая будет использоваться для хранения состояния CPU */ jmp_buf store_cpu; // тип данных jmp_buf определён в заголовочном // файле setjmp.h /* объявим и определим функцию, которая что-то делает, например, обрабатывает ошибку, а затем совершает длинный нелокальный переход */ void holder_err (void) [группа операторов 1] // здесь что-то делается longjmp(store_cpu,15); // здесь восстанавливается состояние CPU и // совершается длинный нелокальный переход в // основную функцию программы // 15 - это значение, которое теперь будет // возвращать функция setjmp. Оно может быть // любым ненулевым /* Основная функция программы */ void main(void) int x; volatile int у; // эта локальная переменная не будет сохранена // после выполнения функции longjmp, т. к. // она объявлена без атрибута volatile // эта локальная переменная будет сохранена // после выполнения функции longjmp, т. к. // она объявлена с атрибутом volatile [группа операторов 2] // здесь что-то делается if (setjmp(store_cpu)==0) // оператор if. Функция setjmp сохраняет // состояние CPU в переменной store_cpu и // возвращает О
5.8. Функции двоично-десятичного преобразования 279 { [группа операторов 3] // здесь что-то делается holder_err(); // вызывается функция holder_err } else // длинный нелокальный переход из [группа операторов 4] //из функции holder_err будет // осуществлен сюда, т. к. функция setjmp // теперь вернёт значение 15, что не // равно 0, т. е. условие оператора if // будет ложным } Обратите внимание, что функция setjmp будет возвращать значение, оп- ределённое в функции longjmp, ТОЛЬКО сразу после вызова функции longjmp. При последующих вызовах функция setjmp будет возвращать 0. 5.8. Функции двоично-десятичного преобразования Перечень функций двоично-десятичного преобразования (BCD) и их дейс- твия приведены в Табл. 5.14. Таблица 5.14. Функции двоично-десятичного (BCD) преобразования Функция Действие unsigned char bcdlbin (unsigned char n) Преобразует число n из BCD представления в его двоичный эквивалент unsigned char bin2bcd (unsigned char n) Преобразует число п из двоичного представления в его BCD эквивалент. Значение числа п должно быть от 0 до 99 Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций определены в файле bcd.h, расположенном в подди- ректории ..\INC. Перед использованием этих функций директивой #inchide дол- жен быть подключён файл bcd.h. Пример: /* Использование функций двоично-десятичного преобразования (BCD) */ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед использованием функций двоично-десятичного преобразования подключим заголовочный файл bcd.h */ #include <bcd.h> /* Основная функция программы */ void main(void)
280 Глава 5. Использование библиотечных функций /* При двоично-десятичном представлении числа каждая тетрада, т. е. 4 бита, соответствует двоичному представлению десятичной цифры. Так, если число ObOOlOOllO интерпретировать как двоично-десятичное, то оно будет обозначать десятичное число 26, т. к. 0010 соответствует 2, а ОНО - б. Если число ObOOlOOllO интерпретировать как двоичное, то оно будет обозначать десятичное число 38 */ unsigned char x,y,z; х=0Ь00100110; // Объявляем символьные переменные (байты) // без знака х, у и z // инициализируем байт х y=bcd2bin(x); // у=26=0Ь00011010=0х1А, т. к. функция bcd2bin // интерпретирует число х как // двоично-десятичное (т. е. считает, что х=26) //и возвращает его двоичный эквивалент z=bin2bcd(x); // z=56=0b00111000-0x38, т. к. функция bin2bcd // интерпретирует число х как // двоичное (т. е. считает, что х=38) //и возвращает его двоично-десятичный } // эквивалент: ООН соответствует 3, а 1000 - 8 5.9. Функции преобразования кода Грея Код Грея — непозиционный код с одним набором символов (0 и 1) для каждо- го бита. Число в коде Грея НЕ ЯВЛЯЕТСЯ суммой цифр. Чтобы показать соот- ветствие последовательности чисел коду Грея, можно воспользоваться таблицей, но есть и наглядное правило построения этой последовательности. Младший бит в последовательности чисел в коде Грея принимает значения 0 и 1, затем следующий старший бит становится единичным и младший бит прини- мает свои значения уже в обратном порядке (1,0). Этим и объясняется название кода — «отражённый». Соответственно два младших бита принимают значения 00, 01, 11, 10, а затем при единичном следующем старшем разряде — те же значе- ния в обратном порядке (10, 11,01, 00). В Табл. 5.15 показаны первые восемь чи- сел в двоичном коде и в коде Грея. Таблица 5.15, Первые восемь чисел в двоичном коде и в коде Грея N Двоичный код Код Грея N Двоичный код Код Грея 0 000 000 4 100 ПО 1 001 001 5 101 111 2 010 011 6 по 101 3 011 010 7 111 100
5.9. Функции преобразования кода Грея 281 Алгоритм перевода чисел в коде Грея в позиционный код прост: каждый бит в позиционном коде равен сумме по модулю 2 этого и всех более старших битов в коде Грея. Старшие биты соответственно совпадают. Перевод из позиционного кода в код Грея ещё проще: каждый бит в коде Грея равен сумме по модулю 2 этого и следующего старшего бита в позиционном коде. Заметьте: код Грея отличается от позиционного кода только интерпретацией значения битов в числе, поэтому числа в разных кодах можно хранить в одних и тех же переменных. Более того, одно и то же значение переменной также можно интерпретировать по-разному — это просто даст разные числа. В CodeVisionAVR предусмотрены специальные функции преобразования кода Грея. Перечень этих функций и их действия приведены в Табл. 5.16. Таблица 5.16. Функции преобразования кода Грея Функция Действие unsigned char gray2binc (unsigned char n) Преобразует символьное без знака число п из представления в коде Грея в его двоичный эквивалент unsigned char gray2bin (unsigned int n) Преобразует целое без знака число п из представления в коде Грея в его двоичный эквивалент unsigned char gray2binl (unsigned long n) Преобразует длинное целое без знака число п из представления в коде Грея в его двоичный эквивалент unsigned char bin2grayc (unsigned char n) Преобразует символьное без знака число п из двоичного представления в его эквивалент в коде Грея unsigned char binlgray (unsigned int n) Преобразует целое без знака число п из двоичного представления в его эквивалент в коде Грея unsigned char bin2grayl (unsigned long n) Преобразует длинное целое без знака число п из двоичного представления в его эквивалент в коде Грея Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций определены в файле gray.h, расположенном в под- директории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл gray.h. Пример: /* Использование функций преобразования кода Грея */ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед использованием функций преобразования кода Грея подключим заголовочный файл gray.h */ ^include <gray.h> /* Основная функция программы */ void main(void) { unsigned char x, y, z; // Объявляем символьные переменные
282 Глава 5. Использование библиотечных функций II без знака (байты данных) х, у, z х=0Ы10; // Инициализируем символьную // переменную без знака (байт данных) х у= gray2binc(х); /* функция gray2binc воспринимает число х как число, представленное в коде Грея, т. е. считает, что х=4, и переводит его в двоичный эквивалент. Это значение присваивается переменной у. Таким образом, у=0Ь100 (см. Табл. 5.15) */ z= bin2grayc(х); /* функция bin2grayc воспринимает число х как число, представленное в двоичном коде, т. е. считает, что х=б, и переводит его в эквивалент в коде Грея. Это значение присваивается переменной z. Таким образом, z=0bl01 (см. Табл. 5.15) */ } 5.10. Функции доступа к памяти Перечень функций доступа к памяти и их действия приведены в Табл. 5.17. Таблица 5,17. Функции доступа к памяти Функция Действие void pokeb (unsigned int addr, unsigned char data) Эта функция записывает данные byte (байтовые) в SRAM по адресу addr void pokew (unsigned int addr, unsigned int data) Эта функция записывает данные word (двухбайтовые) в SRAM по адресу addr LSB (младший байт) записывается по адресу addr, a MSB (старший байт) записывается по адресу addr+1. unsigned char peekb (unsigned int addr) Эта функция читает byte (байт), расположенный в SRAM по адресу addr. unsigned int peekw (unsigned int addr) Эта функция читает word (слово — два байта), расположенное в SRAM по адресу addr LSB (младший байт) читается по адресу addr, a MSB (старший байт) читается по адресу addr+1. Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций определены в файле mem.h, расположенном в под- директории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл mem.h. Пример: /* Использование функций доступа к памяти */ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR
5.17. Функции протокола 7- Wire 283 /* Перед использованием функций доступа к памяти подключим заголовочный файл mem.h */ #include <mem.h> /* Основная функция программы */ void main(void) { unsigned char а=15; // Объявляем и инициализируем символьную // переменную без знака (байт данных) а unsigned char b; // Объявляем символьную переменную // без знака (байт данных) b unsigned int c=2345; // Объявляем и инициализируем целую // переменную без знака (слово данных) с unsigned int d; // Объявляем целую переменную без знака // (слово данных) d unsigned int address_char=0x8F; // Объявляем и инициализируем целую // переменную без знака - адрес // байта данных unsigned int address_int =0х8А; // Объявляем и инициализируем целую // переменную без знака - адрес // слова данных pokeb(address_char, а); // Запишем байт данных а //по адресу address_char pokew(address_int, с); // Запишем слово данных с //по адресу address_int b=peekb(address_char); // Прочитаем байт данных по адресу // address_char в переменную b d=peekw(address_int); // Прочитаем слово данных по адресу // address_int в переменную d } 5.11. Функции протокола 1 -Wire Эти функции предназначены для облегчения пользователю сопряжения меж- ду программами Си и различными периферийными устройствами, использую- щими протокол 1-Wire. Интерфейс 1-Wire (1-проводной) был разработан фирмой Dallas Semiconductors, чтобы уменьшить необходимое количество проводов для сетевых периферийных компонентов. Для обмена данных между различными компонен- тами нужен только один провод. Устройства master (ведущий) и slave (ведомый) в сети 1-Wire, общающиеся по одной линии с подтягивающим резистором 4.7 кОм, показаны на Рис. 5.3.
284 Глава 5. Использование библиотечных функций Vcc Обратите внимание на подтягивающий резистор: без него интерфейс 1- Wire работать не будет! Функции протокола 1-Wire рассматривают микроконтроллер как master (веду- щий) шины, а периферийные устройства — как^/яу^ (ведомые). Перечень функ- ций протокола 1-Wire и их действия приведены в Табл. 5.18. Таблица 5.18. Перечень функций протокола 1-Wire Функция Действие unsigned char wljnit(void) Эта функция инициализирует устройства 1-Wire на шине. Она возвращает 1, если устройства присутствуют, или 0, если устройства отсутствуют unsigned char wl_read(void) Эта функция читает байт из шины 1-Wire unsigned char wl_write (unsigned char data) Эта функция записывает байт data в шину 1-Wire. Она возвращает 1, если процесс записи завершился нормально, или 0, если нет unsigned char wl_search (unsigned char cmd,void *p) Эта функция возвращает количество устройств, подключённых к шине 1-Wire. Если никаких устройств не было обнаружено, то она возвращает 0. Байт cmd представляет команды Search ROM (Поиск ROM-кодов) (FOh), Alarm Search (Поиск аварии) (ECh) для DS 1820/DS18S20 или другие аналогичные команды, посылаемые на устройство 1-Wire. Указатель р указывает на область SRAM, где хранятся 8 байтов ROM-кода, возвращаемого устройством. После восьмого байта функция устанавливает девятый байт статуса, который содержит бит статуса, возвращаемый некоторыми устройствами 1-Wire (например, DS2405). Таким образом, пользователь должен распределить 9 байтов SRAM для каждого устройства, присутствующего на шине 1-Wire. Если к шине 1-Wire подключено более чем одно устройство, то потребитель должен сначала вызвать функцию wl_search, чтобы идентифицировать ROM-коды устройств и чтобы можно было обращаться к ним в программе на последующем этапе unsigned char wl_crc8 (void *p, unsigned char n) Эта функция возвращает 8 битов DOW CRC для блока байтов длиной п, начинающегося с адреса р
5.11. Функции протокола 1- Wire 285 Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций размещаются в файле Iwire.h, расположенном в поддиректории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл Iwire.h. До подключения файла Iwire.h необходимо объявить, какой порт микроконт- роллера и какие биты порта будут использованы для связи через протокол 1-Wire. Синтаксис: #asm .equ __wl_port=aflpec_nopTa .equ __wl_bit=6nT_nopTa_wl #endasm Адрес соответствующего порта можно найти в файле .h, который соответству- ет используемому микроконтроллеру. Файлы с расширением .h расположены в поддиректории ..\INC. Пример: /* Использование функций протокола 1-Wire */ /* Микроконтроллер AT90S2313 */ ^include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /★ Перед подключением файла Iwire.h объявим порт, используемый для подключения шины 1-Wire */ /* Шину 1-Wire подключим к PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; Для сигнала данных используем 0 бит порта */ #asm .equ __wl_port=0xl8 .equ __wl_bit=0 #endasm /* Теперь можно подключать функции 1-Wire */ #include <lwire.h> Поскольку функции 1-Wire для правильной работы требуют прецизионных временных задержек, то в течение их выполнения прерывания должны быть за- прещены. Также очень важно определить правильную тактовую частоту чипа AVR при конфигурировании проекта (см. Закладка С Compiler (Компилятор Си)). Подробнее см. описание на протокол 1-Wire, например, на сайте http://www.elin.ru/l-Wire/rn lan.htm.
286 Глава 5. Использование библиотечных функций Пример: /* Пример программы для доступа к устройствам с протоколом 1-Wire */ /* Микроконтроллер AT90S2313 */ ^include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла lwire.h объявим порт, используемый для подключения шины 1-Wire */ /* Шину 1-Wire подключим к PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; Для сигнала данных используем 0 бит порта */ #asm .equ __wl_port=0xl8 .equ __wl_bit=0 ttendasm /* Теперь можно подключать Функции 1-Wire */ ^include <lwire.h> /* определим максимальное количество устройств, которое может присутствовать на шине 1-Wire */ #define max_number 10 /* распределим пространство SRAM для ROM-кода и бита статуса */ unsigned char cod_rom[max_number][9]; /* Основная функция программы */ void main(void) { unsigned char number_device; // Объявляем переменную number_device, // где будет храниться количество // устройств, присутствующих // на шине 1-Wire /* инициализируем 1-Wire устройства на шине функцией wl_init */ if(wl_init()) /* Следующий составной оператор в фигурных скобках будет выполняться, если функция wl_init вернёт значение, не равное 0, т. е. если на шине 1-Wire присутствует хотя бы одно устройство */ { /* определим, сколько 1-Wire устройств подключено к шине,
5.11. Функции протокола 1- Wire 287 и сохраним их ROM-коды в массиве cod_rom */ number_device=wl_search(Oxf0,cod_rom); /* теперь в переменной number_device хранятся устройства, присутствующие на шине 1-Wire, а в массиве cod_rom - их ROM-коды */ } while (1); } // заканчиваем программу бесконечным циклом 5.11.1. Функции температурного датчика DS1820/DS18S20 от Dallas Semiconductor Эти функции предназначены для облегчения пользователю сопряжения меж- ду программами Си и температурным датчиком DS1820/DS18S20 шины 1-Wire. Перечень функций температурного датчика DS1820/DS18S20 и их действия приведены в Табл. 5.19. Таблица 5.19. Перечень функций температурного датчика DS1820/DS18S20 Функция Действие int dsl820_temperature_10 (unsigned char *addr) Эта функция возвращает температуру датчика DS1820/DS18S20 с ROM-кодом, хранящимся в 8-байтовом массиве, расположенном по адресу addr. Температура выражена в °C и умножена на 10. В случае ошибки функция возвращает значение -9999. Если использован только один датчик, DS1820/DS18S20, то массив ROM-кодов не нужен, а указатель addr должен быть NULL (0) unsigned char dsl820_set_alarm (unsigned char *addr, signed char temp_low, signed char temp_high) Эта функция устанавливает нижнюю (temp_low) и верхнюю (temp_high) пороговую температуру аварийного состояния для DS1820/DS18S20. Если всё в норме, то функция возвращает значение 1, иначе — возвращает 0. Пороговые температуры сохраняются как в сверхоперативном SRAM, так и в EEPROM DS1820/DS18S20. ROM-код, нужный для обращения к устройству, сохраняется в 8-байтовом массиве, расположенном по адресу addr Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций размещаются в файле dsl820.h, расположенном в поддиректории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл dsl820.h. Прототипы функций шины 1-Wire авто- матически подключаются при подключении dsl820.h. До подключения файла dsl820.h необходимо объявить, какой порт микрокон- троллера и какие биты порта будут использованы для связи с DS1820/DS18S20 че- рез шину 1-Wire.
288 Глава 5. Использование библиотечных функций Синтаксис: #asm .equ __wl_port=aflpec_nopTa .equ __wl_bit=6nT_nopTa_wl #endasm Адрес соответствующего порта можно найти в файле .h, который соответству- ет используемому микроконтроллеру. Файлы с расширением .h расположены в поддиректории ..\INC. Пример: /* Использование функций температурного датчика DS1820/DS18S20 */ /* Микроконтроллер AT90S2313 */ ttinclude <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла dsl820.h объявим порт, используемый для подключения шины 1-Wire */ /* Шину 1-Wire подключим к PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; Для сигнала данных используем 0 бит порта */ #asm .equ ____wl_port=0xl8 .equ __wl_bit=0 ttendasm /* Теперь можно подключать функции температурного датчика DS1820/DS18S20 */ #include <dsl820.h> Описание микросхемы DS1820/DS18S20 (Рис. 5.4) можно найти на сайте про- изводителя: http://www.dalsemi.com/. (ВИД СНИЗУ) ч Рис, 5.4. Микросхема DS1820/DS18S20 — внешний вид и назначение выводов.
5.11. Функции протокола 1- Wire 289 Пример подключения DS 1820/DS18S20 к микроконтроллеру AT90S2313 по- казан на Рис. 5.5. Цепь сброса, питания и кварцевый резонатор микроконтролле- ра не показаны (см. Рис. 4.1). DD1 DD2 AT90S2313 DS1820/DS18S20 XTAL1 XTAL2 RESET PD0/RXD PD1/TXD PD2/INT0 PD3/INT1 PD4/T0 PD5/T1 PD6/ICP PB0/AIN0 PB1/AIN1 РВ2 РВЗ/ОС1 РВ4 РВ5 РВ6 РВ7 Рис, 5.5, Пример подключения DS1820/DS18S20 к микроконтроллеру AT90S2313. Если использовано несколько датчиков, то программа сначала должна иден- тифицировать ROM-коды для каждого датчика. Только после этого может быть использована функция dsl820_temperature_10 с указателем addr, указывающим на массив, который содержит ROM-код для соответствующего устройства. Если используется только один датчик, DS1820/DS18S20, то массив ROM-ко- дов не нужен, и указатель addr должен быть NULL (ноль). Аварийное состояние для всех устройств DS1820/DS18S20 на шине 1-Wire можно определить вызовом функции wl_search с командой Alarm Search (Поиск Аварии) (ECh) (см. Функции протокола 1-Wire). Пример: /* Пример программы для доступа к DS1820/DS18S20 */ /* Микроконтроллер AT90S2313, шина 1-Wire DS1820/DS18S20 подключена к 0 биту PORTB (Рис. 5.5) */ ^include <90s2313.h> // подключим заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла dsl820.h объявим порт и бит порта, используемые для подключения шины 1-Wire */ /* Шина 1-Wire подключена к 0 биту PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; */ #asm .equ _wl_port=0xl8 .equ _wl_bit=0 ttendasm /* Теперь можно подключать функции температурного датчика DS1820/DS18S20 */ ^include <dsl820.h>
290 Глава 5. Использование библиотечных функций /★ определим максимальное количество устройств DS1820/DS18S20, присутствующих на шине 1-Wire */ #define max_number 8 /* распределим пространство SRAM для ROM-кода устройств DS1820/DS18S20 и бита статуса Для каждого устройства используется 9 байтов (смотри описание функции wl_search в главе Функции протокола 1-Wire), но только первые 8 байтов содержат ROM-код и CRC (контрольную сумму) */ unsigned char cod_rom[max_number,9]; /* Основная функция программы */ void main(void) { unsigned char i; // Объявляем индексную переменную i unsigned char number_DS1820; // Объявляем переменную // umber_DS1820, // где будут храниться устройства // DS1820/DS18S20, // присутствующие на шине 1-Wire int temper[max_number]; // Объявляем целочисленный массив temper // где будут храниться значения // температур в ’С х 10, измеренных // устройствами DS1820/DS18S20 /* инициализируем DS1820/DS18S20 устройства на шине 1-Wire функцией wl_init */ if(wl_init()) /* Следующие операторы в фигурных скобках будут выполняться, если функция wl_init вернёт значение, не равное 0, т. е. если на шине 1-Wire присутствует ходя бы одно устройство DS1820/DS18S20 (см. Функции протокола 1-Wire) */ { /* определим, сколько устройств DS1820/DS18S20 подключено к шине 1-Wire, и сохраним их ROM-коды в массиве cod_rom Для этого вызываем функцию wl_search с командой Search ROM (Поиск ROM-кодов) (код - FOh) (см. Функции протокола 1-Wire) */ number_DS1820=wl_search(Oxf0,&cod_rom[0][0]); /* теперь в переменной number_DS1820 хранятся устройства DS1820/DS18S20, присутствующие на шине 1-Wire , а в массиве cod_rom - их ROM-коды */ /* измерим температуру и результаты занесём
5.11, Функции протокола 1- Wire 291 в массив temper */ while (1) for (i=0;i<number_DS1820;) { temper[i]=dsl820_temperature_10(&cod_rom[i][0]); }; /* теперь в массиве temper хранятся значения температур в ’С х 10, измеренные всеми устройствами DS1820/DS18S20, присутствующими на шине 1-Wire */ while (1); // заканчиваем программу бесконечным циклом } За более подробной информацией об использовании DS1820/DS18S20 обра- титесь к их описанию (datasheet) на сайте производителя. 5.11.2. Функции EEPROM DS2430 от Dallas Semiconductor Эти функции предназначены для облегчения пользователю сопряжения меж- ду программами Си и EEPROM DS2430 шины 1-Wire. Перечень функций EEPROM DS2430 и их действия приведены в Табл. 5.20. Таблица 5,20, Перечень функций EEPROM DS2430 Функция Действие unsigned char ds2430_read_block (unsigned char *romcode, unsigned char *dest, unsigned char addr, unsigned char size) Эта функция читает блок, состоящий из size-байтов, начинающийся с адреса addr EEPROM-памяти DS2430, и сохраняет его в строке dest, расположенной в SRAM. Она возвращает 1, если всё нормально, если нет — возвращает 0. Устройство DS2430 выбирается, используя его ROM-код, хранящийся в 8-байтовом массиве, расположенном по адресу romcode unsigned char ds2430_read (unsigned char *romcode, unsigned char addr, unsigned char *data) Эта функция читает байт из EEPROM-памяти DS2430 по адресу addr и сохраняет его в ячейке памяти SRAM по указателю data. Она возвращает 1, если всё нормально, если нет — возвращает 0. Устройство DS2430 выбирается, используя его ROM-код, хранящийся в 8-байтовом массиве, расположенном по адресу romcode unsigned char ds2430_write_block (unsigned char *romcode, unsigned char *source, unsigned char addr, unsigned char size) Эта функция записывает блок, состоящий из size-байтов, из строки source, расположенной в SRAM, в EEPROM DS2430, начиная с адреса памяти addr. Она возвращает 1, если всё нормально, если нет — возвращает 0. Устройство DS2430 выбирается, используя его ROM-код, хранящийся в 8-байтовом массиве, расположенном по адресу romcode unsigned char ds2430_write (unsigned char *romcode, unsigned char addr, unsigned char data) Эта функция записывает байт data в EEPROM-память DS2430 по адресу addr. Она возвращает 1, если всё нормально, если нет — возвращает 0. Устройство DS2430 выбирается, используя его ROM-код, хранящийся в 8-байтовом массиве, расположенном по адресу romcode
292 Глава 5. Использование библиотечных функций (продолжение) Функция Действие unsigned char ds2430_read_appreg_block (unsigned char *romcode, unsigned char *dest, unsigned char addr, unsigned char size) Эта функция читает блок, состоящий из size-байтов, начиная с адреса addr прикладного регистра DS2430, и сохраняет его в строке dest, расположенной в SRAM. Она возвращает 1, если всё нормально, если нет — возвращает 0. Устройство DS2430 выбирается, используя его ROM-код, хранящийся в 8-байтовом массиве, расположенном по адресу romcode unsigned char ds2430_write_appreg_block (unsigned char *romcode, unsigned char *source, unsigned char addr, unsigned char size) Эта функция записывет блок, состоящий из size-байтов, из строки source, расположенной в SRAM, в DS2430, начиная с адреса addr прикладного регистра. Она возвращает 1, если все нормально, если нет — возвращает 0. Устройство DS2430 выбирается, используя его ROM-код, хранящийся в 8-байтовом массиве, расположенном по адресу romcode Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций размещаются в файле ds2430.h, расположенном в поддиректории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл ds2430.h. Прототипы функций шины 1-Wire под- ключаются автоматически при подключении ds2430.h. До подключения файла ds2430.h необходимо объявить, какой порт микроконтрол- лера и какие биты порта будут использованы для связи с DS2430 через шину 1-Wire. Синтаксис: #asm .equ __wl_port=aflpec_nopTa .equ __wl_bit=6nT_nopTa_wl #endasm Адрес соответствующего порта можно найти в файле .h, который соответству- ет используемому микроконтроллеру. Файлы с расширением .h расположены в поддиректории ..\INC. Пример: /* Использование функций EEPROM DS2430 */ /* Микроконтроллер AT90S2313 */ ^include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла ds2430.h объявим порт, используемый для подключения шины 1-Wire */ /* Шину 1-Wire подключим к PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; Для сигнала данных используем 0 бит порта */ #asm .equ __wl_port=0xl8 .equ __wl_bit=0
5.11. Функции протокола 1- Wire 293 tfendasm /* Теперь можно подключать функции EEPROM DS2430 */ #include <ds2430.h> Описание микросхемы DS2430 (Рис. 5.6) можно найти на сайте производите- ля: http://www.dalsemi.com/. (ВИД СНИЗУ) Рис. 5.6. Микросхема DS2430 — внешний вид и назначение выводов. Пример подключения DS2430 к микроконтроллеру AT90S2313 показан на Рис. 5.7. Цепь сброса, питания и кварцевый резонатор микроконтроллера не по- казаны (см. Рис. 4.1). DD1 AT90S2313 DD2 DS2430 XTAL1 XTAL2 RESET PDO/RXD PD1/TXD PD2/INT0 PD3/INT1 PD4/T0 PD5/T1 PD6/ICP PBO/AINO PB1/AIN1 РВ2 РВЗ/0С1 РВ4 РВ5 РВ6 РВ7 VCC Рис. 5.7. Пример подключения DS2430 к микроконтроллеру AT90S2313. Для микросхемы DS2430 не требуется отдельного питания, ей ДОСТА- ТОЧНО паразитного напряжения от шины 7- Wire. Если используется только одна EEPROM DS2430, то массив ROM-кодов не нужен, а указатель romcode должен быть NULL (ноль). Если используется несколько устройств 1-Wire, то программа должна сначала идентифицировать ROM-коды для всех устройств. Только после этого могут быть использованы функции DS2430 с указателем romcode, указывающим на массив, который содержит ROM-код для соответствующего устройства.
294 Глава 5. Использование библиотечных функций Пример: /* Пример программы для доступа к DS2430 */ /* Микроконтроллер AT90S2313, шина 1-Wire DS2430 подключена к 0 биту PORTB (Рис. 5.7) */ ttinclude <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла ds2430.h объявим порт и бит порта, используемые для подключения шины 1-Wire */ /* Шина 1-Wire подключена к 0 биту PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; */ #asm .equ __wl_port=0xl8 .equ __wl_bit=0 ttendasm /* Теперь можно подключать функции EEPROM DS2430 */ tfinclude <ds2430.h> /* определим максимальное количество устройств DS2430, которое может присутствовать на шине 1-Wire */ #define max_number 8 /* распределим пространство SRAM для ROM-кода устройств DS2430 и бита статуса Для каждого устройства используется 9 байтов (см. описание функции wl_search в главе Функции протокола 1-Wire), но только первые 8 байтов содержат ROM-код и CRC (контрольную сумму) */ unsigned char cod_rom [max_number][9]; int data_write=0xl234; // Объявляем и определяем целую переменную и data„write, значение которой будем и записывать во все EEPROM DS2430 int data_read [max_number]; II Объявляем целочисленный массив, в II который будем считывать значения II изо всех EEPROM DS2430 #define address_0 1 // Определим (произвольно) начальный // адрес памяти EEPROM DS2430, начиная с // которого будем осуществлять операции // записи и чтения /* Основная функция программы */ void main(void) { unsigned char i; // Объявляем индексную переменную i unsigned char number_DS2430; // Объявляем переменную number_DS2430,
5.11. Функции протокола 1-Wire 295 // где будет храниться количество // устройств DS2430, присутствующих //на шине 1-Wire /* инициализируем DS2430 устройства на шине 1-Wire функцией wl_init */ if(wl_init()) /* Следующие операторы в фигурных скобках будут выполняться, если функция wl_init вернёт значение, не равное 0, т. е. если на шине 1-Wire присутствует хотя бы одно устройство DS2430 (см. Функции протокола 1-Wire) */ { // открывающая скобка оператора if /* определим, сколько устройств DS2430 подключено к шине 1-Wire, и сохраним их ROM-коды в массиве cod_rom Для этого вызываем функцию wl_search с командой Search ROM (Поиск ROM-кодов) (код - FOh)(см. Функции протокола 1-Wire) */ number_DS2430=wl_search(0xf0,cod_rom); /* теперь в переменной number_DS2430 хранятся устройства DS2430, присутствующие на шине 1-Wire, а в массиве cod_rom - их ROM-коды */ /* запишем значение data_write в каждую DS2430 */ for (1=0;i<number_DS2430;i++) { // открывающая скобка оператора for /* В функции ds2430_write_block: поочередно выбираем ROM-код каждого устройства(i) из массива cod_rom. В качестве указателя подставляем адрес первого байта i-ro ROM-кода (&cod_rom[i][0]); записываемая строка - data_write. В качестве указателя подставляем адрес (&data_write); запись начинаем с адреса address_0; значение size=2, т. к. в целой 2 байта */ ds2430_write_block(&cod_rom[i][0],&data_write,address_0,2); } // закрывающая скобка оператора for /* считаем записанное значение из каждой DS2430 в массив data_read */ for (i=0;i<number_DS2430;i++) { // открывающая скобка оператора for /* В функции ds2430_read_block: поочередно выбираем ROM-код каждого устройства(i) из массива cod. rom. В качестве указателя подставляем адрес первого байта i-ro ROM-кода (&cod_rom[i][0]); считанное значение записываем в соответствующую переменную data_read[i], заполняя массив data_read. В качестве указателя подставляем адрес
296 Глава 5. Использование библиотечных функций (&data_read[i]); чтение начинаем с адреса address_O; значение size=2, т. к. в целой 2 байта */ ds2430_read_block(&cod_rom[i, 0], &data_read[i],address_0,2); } И закрывающая скобка оператора for }; II закрывающая скобка оператора if while (1); и заканчиваем программу бесконечным циклом } За более подробной информацией об использовании DS2430 обратитесь к их описанию (datasheet) на сайте производителя. 5.11.3. Функции EEPROM DS2433 от Dallas Semiconductor Эти функции предназначены для облегчения пользователю сопряжения меж- ду программами Си и EEPROM DS2433 шины 1-Wire. Перечень функций EEPROM DS2433 и их действия приведены в Табл. 5.21. Таблица 5.21. Перечень функций EEPROM DS2433 Функция Действие unsigned char ds2433_read (unsigned char *romcode, unsigned int addr, unsigned char *data) Эта функция читает байт из EEPROM-памяти DS2433 по адресу addr и сохраняет его в ячейке памяти SRAM по указателю data. Она возвращает 1, если все нормально, если нет — возвращает 0. Устройство DS2433 выбирается, используя его ROM-код, хранящийся в 8-байтовом массиве, расположенном по адресу romcode unsigned char ds2433_write_block (unsigned char *romcode, unsigned char *source, unsigned int addr, unsigned int size) Эта функция записывает блок, состоящий из size-байтов, из строки source, расположенной в SRAM, в EEPROM DS2433, начиная с адреса памяти addr. Она возвращает 1, если все нормально, если нет — возвращает 0. Устройство DS2433 выбирается, используя его ROM-код, хранящийся в 8-байтовом массиве, расположенном по адресу romcode unsigned char ds2433_write (unsigned char *romcode, unsigned int addr, unsigned int addr, unsigned int size) Эта функция записывает байт data в EEPROM-память DS2433 по адресу addr. Она возвращает 1, если все нормально, если нет — возвращает 0. Устройство DS2433 выбирается, используя его ROM-код, хранящийся в 8-байтовом массиве, расположенном по адресу romcode Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций размещаются в файле ds2433.h, расположенном в поддиректории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл ds2433.h. Прототипы функций шины 1-Wire авто- матически подключаются при подключении ds2433.h. До подключения файла ds2433.h необходимо объявить, какой порт микрокон- троллера и какие биты порта будут использованы для связи с DS2433 через шину 1-Wire.
5.11. Функции протокола 1- Wire 297 Синтаксис: #asm .equ __wl_port=aflpec_nopTa .equ __wl_bit=6nT_nopTa_wl tfendasm Адрес соответствующего порта можно найти в файле .h, который соответству- ет используемому микроконтроллеру. Файлы с расширением .h расположены в поддиректории ..\INC. Пример: /* Использование функций EEPROM DS2433 */ /★ Микроконтроллер AT90S2313 ★/ tfinclude <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /★ Перед подключением файла ds2433.h объявим порт, используемый для подключения шины 1-Wire ★/ /★ Шину 1-Wire подключим к PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; Для сигнала данных используем 0 бит порта ★/ #asm .equ _wl_port=0xl8 .equ _wl_bit=0 ttendasm /★ Теперь можно подключать функции EEPROM DS2433 */ tfinclude <ds2433.h> Описание микросхемы DS2433 (Рис. 5.8) можно найти на сайте производите- ля: http://www.dalsemi.com/. (ВИД СНИЗУ) Рис. 5.8. Микросхема DS2433 — внешний вид и назначение выводов.
298 Глава 5. Использование библиотечных функций Пример подключения DS2433 к микроконтроллеру AT90S2313 показан на Рис. 5.9. Цепь сброса, питания и кварцевый резонатор микроконтроллера не по- казаны (см. Рис. 4.1). DD1 AT90S2313 DD2 DS2433 -JL XTAL1 —— XTAL2 RESET PD0/RXD PD1/TXD PD2/INT0 PD3/INT1 PD4/T0 PD5/T1 PD6/ICP PBO/AINO PB1/AIN1 РВ2 РВЗ/ОС1 РВ4 РВ5 РВ6 РВ7 Рис, 5.9, Пример подключения DS2433 к микроконтроллеру AT90S2313. Для микросхемы DS2433 не требуется отдельного питания, ей ДОСТА- ТОЧНО паразитного напряжения от шины 1- Wire. Если используется только одна EEPROM DS2433, то массив ROM-кодов не нужен, а указатель romcode должен быть NULL (ноль). Если используется несколько устройств 1-Wire, то программа должна сначала идентифицировать ROM-коды для всех устройств. Только после этого могут быть использованы функции DS2433 с указателем romcode, указывающим на массив, который содержит ROM-код для соответствующего устройства. Пример: /* Пример программы для доступа к DS2433 */ /* Микроконтроллер AT90S2313, шина 1-Wire DS2433 подключена к 0 биту PORTB (Рис. 5.9) */ tfinclude <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла ds2433.h объявим порт и бит порта, используемые для подключения шины 1-Wire */ /* Шина 1-Wire подключена к 0 биту PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; */ #asm .equ _____wl_port=0xl8 .equ _wl_bit=0 ttendasm /* Теперь можно подключать функции EEPROM DS2433 */
5.11. Функции протокола 1- Wire 299 ttinclude <ds2433.h> /* определим максимальное количество устройств DS2433, которое может присутствовать на шине 1-Wire */ #define max_number 8 /* распределим пространство SRAM для ROM-кода устройств DS2433 и бита статуса Для каждого устройства используется 9 байтов (см. описание функции wl_search в главе Функции протокола 1-Wire), но только первые 8 байтов содержат ROM-код и CRC (контрольную сумму) */ unsigned char cod_rom [max_number][9]; unsigned char data_write[]="ABC"; // Объявляем и определим строку // data_write, значение // которой будем записывать //во все EEPROM DS2433 unsigned char data_read [max_number][2];// Объявляем символьный массив, //в который будем считывать // значения изо всех EEPROM // DS2433 ttdefine address_0 1 // Определим (произвольно) начальный // адрес памяти EEPROM DS2433, начиная с // которого будем осуществлять операции // записи и чтения /* Основная функция программы */ void main(void) { unsigned char i; // Объявляем индексную переменную i unsigned char number_DS2433; // Объявляем переменную number_DS2433, // где будет храниться количество // устройств DS2433, присутствующих //на шине 1-Wire /* инициализируем DS2433 устройства на шине 1-Wire функцией wl_init */ if(wl_init()) /* Следующие операторы в фигурных скобках будут выполняться, если функция wl_init вернёт значение, не равное 0, т. е. если на шине 1-Wire присутствует хотя бы одно устройство DS2433 (см. Функции протокола 1-Wire) */ { /* определим, сколько устройств DS2433 подключено к шине 1-Wire, и сохраним их ROM-коды в массиве cod_rom Для этого вызываем функцию wl_search с командой Search ROM (Поиск ROM-кодов) (код - FOh)
300 Глава 5. Использование библиотечных функций (см. Функции протокола 1-Wire) */ number_DS2433=wl_search(Oxf0, cod_rom); /* теперь в переменной number_DS2433 хранятся устройства DS2433, присутствующие на шине 1-Wire, а в массиве cod_rom - их ROM-коды */ /* запишем значение строки data_write в каждую EEPROM DS2433 */ for (i=0;i<number_DS2433;i++) { // открывающая скобка оператора for /* В функции ds2433_write_block: поочередно выбираем ROM-код каждого устройства (i) из массива cod_rom. В качестве указателя подставляем адрес первого байта i-ro ROM-кода (&cod_rom[i][0]); записываемая строка - data_write. В качестве указателя подставляем адрес первого элемента этой строки (&data_write[ 0 ] ); запись начинаем с адреса address_0; значение size=3, т. к. в этой строке 3 байта */ ds2433_write_block(&cod_rom[i][0],&data_write[0],address_0, 3); } // закрывающая скобка оператора for /* считаем записанное значение из каждой DS2433 в массив data_read */ for (i=0;i<number_DS2433;i++) { // открывающая скобка оператора for /* В функции ds2433_read—block: поочередно выбираем ROM-код каждого устройства (i) из массива cod_rom. В качестве указателя подставляем адрес первого байта i-ro ROM-кода (&cod_rom[i][0]); считанное значение записываем в соответствующую переменную data_read[i][2], заполняя массив data_read. В качестве указателя подставляем адрес первого элемента в строке(&data_read[i][0]); чтение начинаем с адреса address_0; значение size=3, т. к. в считываемой строке 3 байта */ ds2433_read_block(&cod_rom[i,0],&data_read[i][0],address_0,3); } // закрывающая скобка оператора for }; // закрывающая скобка оператора if while (1); // заканчиваем программу бесконечным циклом } За более подробной информацией об использовании DS2433 обратитесь к её описанию (datasheet) на сайте производителя.
5.12. Функции SPI 301 5.12. Функции SPI Эти функции предназначены для облегчения пользователю сопряжения меж- ду программами Си и различными периферийными устройствами, использую- щими шину SPI. Интерфейс SPI использует три линии для последовательной связи. Дополнительно к многочисленным устройствам памяти все хорошо извест- ные производители предлагают совместимые с SPI аналого-цифровые (АЦП) и цифро-аналоговые (ЦАП) преобразователи, часы реального времени (RTC) и другие устройства. Связи, требующиеся в типовом SPI-интерфейсе, показаны на Рис. 5.10. Мик- роконтроллер (MCU) посылает последовательные данные через свою MOSI (Master Out Slave In — Выход ведущего/Вход ведомого) линию на вход SI (Slave In — Вход ведомого) периферийного устройства. Периферийное устройс- тво посылает свои данные через выход SO (Slave Out — Выход ведомого) на ли- нию MISO. Обмен данных синхронизируется сигналом тактовой частоты SCK, который генерирует микроконтроллер. Сигналы Chip Select (Выбор чипа) SS0...SS3 активизируют периферийное ус- тройство для доступа. мси (master) Рис. 5.10. Последовательный SPI-интерфейс.
302 Глава 5. Использование библиотечных функций Пример синхронизации для обмена данных между микроконтроллером (master) и периферийным (slave) устройством по интерфейсу SPI показан на Рис. 5.11. Функции, которыми требуется управлять через SPI, зависят от используемого периферийного устройства. Интерфейс SPI работает подобно 8-битовому регистру сдвига. Байт, который требуется послать, сохраняется в регистре и будет перемещаться бит за битом на вывод MOSI. Освобождающиеся позиции заполняются битами, полученными с вывода MISO. Для передачи байта в регистр потребуется 8 тактов. Некоторые микроконтроллеры AVR имеют встроенный SPI. Если должно быть использовано это внутреннее периферийное устройство, то следует соот- ветствующим образом сконфигурировать выводы микроконтроллера. Микроконтроллеры с внутренними аппаратными средствами SPI допускают конфигурацию полярности и фазы тактовой частоты SCK. При отсутствии аппарат- ного SPI правильность синхронизации сигналов должна быть запрограммирована. Весь обмен данными SPI организовывает SPI Control Register (Регистр управ- ления SPI) - SPCR (Рис. 5.12). Бит 7 — 1 — 1 4 ~ 1 — 1 ~Г~] 0 $0D($2D) Ш11 Чтение/Запись R/W R/W | R/W | R/W R/W | R/W R/W R/W Начальное значение 0 0 0 0 0 0 0 0 Рис. 5.12. Биты регистра SPCR. Биты управляющего регистра SPI устанавливаются следующим образом. • Установка бита SPIE (SPI Interrupt Enable — Разрешение прерывания SPI) в 1 разрешает прерывание SPI.
5.72 Функции SPI 303 • Установка бита SPE (SPI Enable — Разрешение SPI) в 1 разрешает SPI. Этот бит должен быть установлен для разрешения любой SPI-операции. • Если бит DORD (Data Order — Порядок данных) установлен в 1, то первым будет передаваться младший байт (LSB). Если этот бит сброшен в 0, то пер- вым будет передаваться старший байт (MSB). • Бит MSTR (Master/Slave Select — Выбор Master/Slave) выбирает режим SPI, в котором работает микроконтроллер: 1 — режим Master, 0 — режим Slave. • Бит CPOL (Clock Polarity — Полярность тактовых импульсов) определяет поляр- ность тактовых импульсов: 1 — по высокому уровню, 0 — по низкому уровню. • Бит СРНА (Clock Phase — Фаза тактовых импульсов) определяет фазу так- товых импульсов: 1 — по началу цикла, 0 — по половине цикла. • Биты SPR1 и SPRO (SPI Clock Rate Select 1 и 2 — Выбор показателя такто- вой частоты SPI) определяют показатель тактовой частоты SPI, когда мик- роконтроллер в режиме master. Соотношение между тактовой частотой микроконтроллера и SCK, в зависимости от значения этих битов, приведе- но в Табл. 5.22. Таблица 5.22. Соотношение между тактовой частотой микроконтроллера и SCK SPR1 SPR0 SCK 0 0 Fcl/4 0 1 Fcl/16 1 0 Fcl/64 1 1 Fcl/128 Пример: /* Пример конфигурации SPI Control Register (Регистра управления SPI) - SPCR */ /* Чип: AT90S8515 Тактовая частота: 4 МГц */ // запретим прерывания - SPIE=0, // разрешим SPI - SPE=1, // первым будем передавать старший байт (MSB) - DORD=0, // инициализируем микроконтроллер в режиме master (мастера)- MSTR=1, // полярность тактовых импульсов по низкому уровню - CPOL=0, // фаза тактовых импульсов по началу цикла - СРНА=1, // частота тактовых импульсов SCK=fxtal/4 (частота кварца/4) - SPRl=SPR0=0 /* Таким образом, в регистр SPCR требуется записать код 0601010100=0x54 */ SPCR=0x54; // регистр SPCR сконфигурирован Более подробно см. описание от Atmel на соответствующий микроконтроллер (см. Команда Help -> AVR Data Sheets (Помощь -> Описания AVR)). Перечень функций SPI и их действия приведены в Табл. 5.23.
304 Глава 5. Использование библиотечных функций Таблица 5.23. Перечень функций SPI Функция Действие Эта функция посылает байт data, одновременно принимая байт. До использования функции spi должен быть сконфигурирован SPI Control Register (Регистр управления SPI) SPCR, согласно unsigned char spi (unsigned char data) описанию от Atmel (см. выше). Поскольку функция spi для SPI-связи использует опрос, то нет необходимости устанавливать SPI Interrupt Enable Bit (Бит разрешения прерывания SPI) SPIE Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций размещаются в файле spi.h, расположенном в под- директории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл spi.h. Рассмотрим пример того, как осуществить доступ к АЦП AD7896 фирмы Analog Devices (Рис. 5.13). Описание этой микросхемы можно найти на сайте производителя: http://www.analog.com/. VlN С 1 > 8 JBUSY VddQ 2 0 7 3 CONVST AGND С 3 S 6 ] DGND SCLK С 4 О) 5 ] SDATA в) Рис. 5.13. Микросхема AD7896 — внешний вид (а) и назначение выводов (б). Пример подключения АЦП AD7896 к микроконтроллеру AT90S8515 показан на Рис. 5.14. Цепи питания, сброса и кварцевый резонатор микроконтроллера не показаны (см. Рис. 4.2). Пример: /* Пример использования функции spi для сопряжения с АЦП AD7896 */ /* Чип: 90S8515 Модель памяти: SMALL Размер стека данных: 128 bytes Тактовая частота: 4 МГц */ /* АЦП AD7896, использующий шину SPI, подключён в соответствии с Рис. 5.14. */ tfinclude <90s8515.h> // подключаем заголовочный файл используемого // микроконтроллера AVR
5.12. Функции SPI 305 ttinclude <spi.h> // подключаем функции SPI /* Определим опорное напряжение VREF AD7896 в мВ */ ttdefine VREF 5000L /* Определим биты ПОРТА В для управляющих сигналов АЦП AD7896 */ tfdefine _busy PINB.O #define n_convst PORTB.1 /* Определим функцию чтения из АЦП AD7896 */ unsigned int ADC_read(void) { unsigned int voltage; // определяем целую беззнаковую переменную // voltage, в которой будет храниться значение // напряжения, измеренное АЦП AD7896 /* начало преобразования в режиме 1 (высокое разрешение) */ n_convst =0; // формируем отрицательный импульс на выводе n_convst =1; // NCONVST, по которому АЦП AD7896 начинает // преобразование /* ждём завершения преобразования, т. е. пока значение сигнала BUSY не станет равным 0 */ while (_busy); /* считаем результат преобразования из АЦП AD7896 */ voltage=spi(0) ; и читаем MSB (старший байт), и используя функцию spi voltage=voltage< <8; 11 сдвигаем считанный байт на бит 11 8, т. е. переместим его в старший 11 байт переменной voltage voltage 1=spi(0); // читаем LSB (младший байт), и используя функцию spi, выполняем 11 побитное ИЛИ со значением // переменной voltage и полученное и значение присваиваем переменной // voltage /* Таким образом, в старшем байте переменной voltage записан MSB результата преобразования АЦП AD7896, а в младшем байте - LSB */ /* Вычислим напряжение в мВ */ /* Чтобы не потерять значащие цифры, вычисления проведём с беззнаковыми длинными целыми (unsigned long), затем результат преобразуем в беззнаковое целое (unsigned) */ voltage=(unsigned) (((unsigned long) voltage*VREF)/4096L); /* вернём измеренное напряжение в мВ */ return voltage;
306 Глава 5. Использование библиотечных функций } /* Основная функция программы */ void main(void) { unsigned int voltage_ADC; // Объявляем переменную voltage_ADC, // где будет храниться значение // напряжения, измеренное АЦП AD7896 /* инициализируем PORTB 0 бит - вход сигнала BUSY из АЦП AD7896; 1 бит - выход сигнала /CONVST на АЦП AD7896; 2 бит - вход (свободный); 3 бит - вход (свободный); 4 бит - выход (он зарезервирован под сигнал SPI/SS на случай наличия другого устройства на шине SPI); 5 бит - вход (свободный); б бит - вход сигнала SDATA из АЦП AD7896 (SPI MISO); 7 бит - выход сигнала SCLK на АЦП AD7896 (SPI SCK) */ DDRB=0x92; /* инициализируем SPI прерывания - запрещены; SPI - разрешён; порядок данных - MSB первый; режим - master (мастера); полярность тактовых импульсов - отрицательная; фаза тактовых импульсов - по середине цикла; тактовая частота SPI - SCK=fxtal/4 (кварц/4) */ SPCR=0x50; /* инициализируем ПОРТ В АЦП AD7896 будет работать в режиме 1 (высокое разрешение) Начальные значения сигналов: /CONVST=1, SCLK=0 */ PORTB=2; /* считаем входное напряжение АЦП AD7896 */ voltage_ADC = ADC_read(); // далее в программе можно использовать // текущее значение измеренного напряжения } За более подробной информацией об использовании AD7896 обратитесь к их описанию (datasheet) на сайте производителя.
5.13. Функции шины 12С 307 Рис. 5.14. Пример подключения АЦП AD7896 к микроконтроллеру AT90S8515. 5.13. Функции шины 12С Функции 12С предназначены для облегчения пользователю сопряжения меж- ду программами Си и различными периферийными устройствами, использую- щими шину 12С. Шина 12С была разработана фирмой Philips для обмена данными между раз- личными устройствами, такими как EEPROM, RAM, AD- и DA-преобразовате- лями (АЦП и ЦАП), RTC (Часы реального времени), и микроконтроллерами в сетевой среде. Связи, требующиеся в типовой сетевой шине 12С, показаны на Рис. 5.15. Ли- нии SDA и SCL, подключённые через подтягивающие резисторы к напряжению питания VCC, соединяют все элементы сети. Сетевая шина 12С может соединять различные /лау/ег-устройства (ведущие), т. е. любые устройства 12С, управляющие обменом данных (например, микроконтроллер), с различными $/яге-устройства- ми (ведомыми), т. е. любыми управляемыми устройствами 12С. Протокол 12С от- вечает за адресацию каждого узла. Рис. 5.15. Сетевая шина 12С.
308 Глава 5. Использование библиотечных функций Slave-адрес каждого устройства шины 12С (Рис. 5.16) представляет собой байт, где четыре старших бита являются Идентификатором типа устройства (Device Type Identifier). Значение следующих трёх битов является адресом устройства. Са- мый младший бит s/ave-адреса определяется тем, что хочет делать основное уст- ройство master— читать или записывать. При чтении этот бит — 1, при записи — 0. Slave-адрес Идентификатор типа устройства Адрес устройства Чтение/ запись Бит 7 6 5 4 3 2 1 0 Значение Рис. 5.16. Slave-адрес устройства шины 12С. Например, EEPROM семейства 24Схх имеет ^ve-адрес, показанный на Рис. 5.17. Slave-адрес Идентификатор типа устройства Адрес устройства Чтение/ запись Бит 7 | 6 5 4 3 2 1 0 Значение Рис. 5.17. Slave-адрес EEPROM семейства 24Схх. Для семейства 24Схх идентификатор типа устройства — 1010. Часть .s/ave-адреса А0...А2 является адресом устройства. Для того чтобы опре- делить адрес устройства, следует подключить его соответствующие выводы — А2, А1 и АО к GND или через подтягивающие резисторы к VCC. Последний бит (R/W) определяет операцию чтения или записи. Каждая операция начинается со стартового условия (START) (Рис. 5.18). Стартовое условие определяется спадающим фронтом на SDA, когда SCL= ВЫСОКИЙ. Каждое устройство шины 12С постоянно определяет уровни на линиях SDA и SCL, чтобы обнаружить действительное стартовое условие. Если обнаружено неправильное стартовое условие, никакие устройства не отвечают. START STOP Рис. 5.18. Стартовое (START) и стоповое (STOP) условия. Первый байт после стартового условия — s/яге-адрес, показывающий доступ к адресуемому устройству. Второй байт посылает адрес ячейки памяти в адресуемом устройстве для последующей операции чтения или записи. При операции записи байт данных посылается как третий байт.
5.13. Функции шины 12С 309 При операции чтения после стартового условия 5/дуе-адреса и адреса ячейки памяти в адресуемом устройстве должно быть послано новое стартовое условие. Первый байт после этого нового стартового условия — 5/дуе-адрес, и он должен указывать доступ чтения к ячейке памяти, адресованной ранее. Каждый обмен данных заканчивается стоповым условием (STOP) (Рис. 5.18). Стоповое условие определяется нарастающим фронтом на линии SDA, когда SCL= ВЫСОКИЙ. Стоповое условие к тому же переключает EEPROM семейс- тва 24Схх на сохранение текущего режима standby (ожидание). Функции шины 12С рассматривают микроконтроллер как master (ведущий) шины, а периферийные устройства — как slaves (ведомые). Перечень функций шины 12С и их действия приведены в Табл. 5.24. Таблица 5.24. Перечень функций шины 12С Функция Действие void i2c_init(void) Эта функция инициализирует шину 12С. Эта функция должна быть вызвана до использования других функций 12С unsigned char i2c_start(void) Эта функция задаёт условие START Возвращает 1, если шина свободна, или 0, если шина 12С занята void i2c_stop(void) Эта функция задаёт условие STOP unsigned char i2c_read (unsigned char ack) Эта функция читает байт из шины. Параметр ack определяет, должно ли быть выдано подтверждение после того, как байт был прочитан. Для отмены подтверждения следует установить ack в 0, для подтверждения — в 1 unsigned char i2c_write (unsigned char data) Эта функция записывает байт data в шину. Возвращает 1, если slave (ведомый) выдаёт подтверждение, или 0, если нет Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций размещаются в файле i2c.h, расположенном в под- директории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл i2c.h. До подключения файла i2c.h необходимо объявить, какой порт микроконт- роллера и какие биты порта будут использованы для связи через шину 12С. Синтаксис: #asm .equ _i2c_port=aflpec_nopTa .equ _sda_bit=6nT_nopTa_sda .equ _scl_bit=6wT_iiopTa_scl ttendasm Адрес соответствующего порта можно найти в файле .h, который соответству- ет используемому микроконтроллеру. Файлы с расширением .h расположены в поддиректории ..\INC.
310 Глава 5. Использование библиотечных функций Пример: /* Использование функций шины I2C */ /* Микроконтроллер AT90S2313 */ ttinclude <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла i2c.h объявим порт, используемый для подключения шины I2C */ /* Шина I2C подключена к PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; Для сигнала sda используем 0 бит порта, для scl - 1 бит порта */ #asm .equ _i2c_port=0xl8 .equ _sda_bit=0 .equ _scl_bit=l tfendasm /* Теперь можно подключать функции шины I2C */ #include .<i2c.h> Из-за необходимости операций чтения и записи устройства памяти хорошо подходят для описания работы шины 12С. Рассмотрим пример того, как осуще- ствить доступ к I2C EEPROM фирмы Atmel 24С01 на 128 байт (Рис. 5.19). Описа- ние этой микросхемы можно найти на сайте производителя: http://www.atmel.com. а) АО С 1 "Т7" 8 ]Vcc А1 С 2 ГО 7 ]WP о А2[ 3 о 6 ]SCL GNDC 4 5 ] SDA б) Рис. 5.19. Микросхема 24С01 — внешний вид (а) и назначение выводов (б). Пример подключения I2C EEPROM 24С01 к микроконтроллеру AT90S2313 показан на Рис. 5.20. Цепи питания, сброса и кварцевый резонатор не показаны (см. Рис. 4.1). Для осуществления доступа к EEPROM фирмы Atmel 24С01 необходимо пос- ледовательно выполнить следующие операции: • сгенерировать стартовое (START) условие (функция i2c_start); • передать в шину 12С адрес slave устройства (микроконтроллер — master, EEPROM — slave). • передать в шину 12С адрес ячейки памяти EEPROM;
5.13. Функции шины 12С 311 • если осуществляется операция чтения, то снова сгенерировать стартовое (START) условие (функция i2c_start) и передать в шину 12С адрес slave-уст- ройства; • осуществить операцию чтения или записи; • сгенерировать стоповое (STOP) условие (функция i2c_stop); • для операции записи осуществить задержку 10 мс. DD1 AT90S2313 4 2 6 7 9 DD2 24С01 XTAL1 XTAL2 RESET PD0/RXD PD1/TXD PD2/INT0 PD3/INT1 РО4Д0 РО5Д1 PD6/ICP PB0/AIN0 PB1/AIN1 РВ2 PB3/OC1 РВ4 РВ5 РВ6 12 13 77 16 18 Рис. 5.20. Пример подключения EEPROM 24С01 к микроконтроллеру AT90S2313. Пример: /* Пример программы для доступа к I2C EEPROM 24С01 от Atmel на 128 байт */ /* Микроконтроллер AT90S2313, шина I2C подключена к PORTB. Для сигнала sda используем 0 бит порта, для scl - 1 бит порта (Рис. 5.20) */ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла i2c.h, объявим порт, используемый для подключения шины 12С */ /* Шина I2C подключена к PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; Для сигнала sda используем 0 бит порта, для scl — 1 бит порта */ #asm .equ __i2c_port=0xl8 .equ __sda_bit=0 .equ __scl_bit=l ttendasm /* теперь можно подключить функции I2C */ ^include <i2c.h> /* Для осуществления задержки в 10 мс понадобится функция delay_ms. Для этого подключим заголовочный файл delay.h */ ^include <delay.h>
312 Глава 5. Использование библиотечных функций ttdefine addres_eep ОхаО //В соответствии с описанием на 24С01 определим // адрес для EEPROM: 0Ь10100000=0ха0. Это адрес // для записи. Адрес для чтения: 0Ь10100001=0ха1 typedef unsigned char byte;// Для удобства определим тип данных byte, // byte = unsigned char /* прочитаем байт из EEPROM */ /* Определим функцию read_eep, которая читает байт из EEPROM по адресу address_byte и возвращает его значение*/ byte read_eep(byte address_byte) { byte data; // Определяем переменную data, в которой будет // храниться считанное значение i2c_start(); // Генерируем стартовое условие i2c_write(addres_eep); // Передаём в шину I2C адрес EEPROM с младшим // байтом = 0, т. к. следующая операция - запись i2c_write(address_byte); // Передаём в шину I2C адрес байта, // который требуется прочитать i2c_start(); // Генерируем стартовое условие i2c_write(addres_eep I 1);// Изменяем самый младший байт адреса // EEPROM на 1, т. к. будем производить // операцию чтения data=i2c_read(0); // Читаем байт из шины I2C в переменную data i2c_stop(); // Генерируем стоповое условие return data; // Возвращаем значение переменной data } /* запишем байт в EEPROM */ /* Определим функцию write_eep, которая записывает байт data в EEPROM по адресу address_byte. Так как функция ничего не возвращает, объявим ее как void */ void write_eep(byte address_byte, byte data) { i2c_start(); и Генерируем стартовое условие i2c_write(addres_eep); // Передаём в шину I2C адрес EEPROM i2c_write(address_byte); и Передаём в шину I2C адрес ячейки, и куда требуется записать байт i2c_write(data) ; и Записываем байт данных data i2c_stop(); II Генерируем стоповое условие delay_ms(10); И завершаем операцию записи задержкой 10 мс } /* Основная функция программы */ void main(void) { byte x; // Определяем переменную x i2c_init(); // Инициализируем шину I2C
5.13. Функции шины I2C 313 write_eep(0x12,0x34); // // С помощью функции write_eep записываем байт 34h по адресу 12h x=read_eep(0x3 4); // И 11 while (1); // С помощью функции read_eep читаем байт по адресу 12h в переменную х В результате х = 34h завершаем основную функцию бесконечным циклом } За более подробной информацией об использовании 24С01 обратитесь к их описанию (datasheet) на сайте производителя. 5.13.1. Функции температурного датчика LM75 от National Semiconductor Эти функции предназначены для облегчения пользователю сопряжения меж- ду программами Си и температурным датчиком LM75 шины 12С. Перечень функций температурного датчика LM75 и их действия приведены в Табл. 5.25. Таблица 5.25. Перечень функций температурного датчика LM75 Функция Действие void lm75_init (unsigned char chip, signed char thyst, signed char tos, unsigned char pol) Эта функция инициализирует чип LM75. Прежде чем вызывать эту функцию, шина 12С должна быть инициализирована вызовом функции i2c_init. Это первая функция, которая должна быть вызвана до использования других функций LM75. Если к шине 12С подключено более одного чипа, то функция должна быть вызвана к каждому чипу, определив соответствующий функциональный параметр chip. К шине 12С можно подключить максимум 8 чипов LM75, их chip-адрес может быть от 0 до 7. Адрес задаётся выводами А0...А2. LM75, сконфигурированный в режиме компаратора, будет функционировать подобно термостату. Выход O.S. становится активным, когда температура превышает предел tos и выходит из активного состояния, когда температура падает ниже предела thyst. Как thyst, так и tos выражены в °C. Параметр pol представляет полярность O.S. выхода LM75 в активном состоянии. Если pol — 0, активным является низкий уровень, и если pol — 1, активным является высокий уровень. Более подробно см. описание (data sheet) на LM75 int lm75_temperature_10 (unsigned char chip) Эта функция возвращает температуру датчика LM75 с адресом chip. Температура выражена в °C и умножена на 10 Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций размещаются в файле lm75.h, расположенном в поддиректории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл lm75.h. Прототипы функций шины 12С автомати- чески подключаются при подключении lm75.h.
314 Глава 5. Использование библиотечных функций До подключения файла lm75.h необходимо объявить, какой порт микроконт- роллера и какие биты порта будут использованы для связи с LM75 через шину 12С. Синтаксис: #asm .equ _12с_рогС=адрес_порта .equ _sda_bit=6nT_nopTa_sda .equ _scl_bit=6wT_nopTa_scl tfendasm Адрес соответствующего порта можно найти в файле .h, который соответству- ет используемому микроконтроллеру. Файлы с расширением .h расположены в поддиректории ..\INC. Пример: /* Использование функций температурного датчика LM75 */ /* Микроконтроллер AT90S2313 */ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла lm75.h объявим порт, используемый для подключения шины I2C */ /* Шина I2C подключена к PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; Для сигнала sda используем 0 бит порта, для scl - 1 бит порта */ #asm .equ _i2c_port=0xl8 .equ _sda_bit=0 .equ _scl_bit=l tfendasm /* Теперь можно подключать функции температурного датчика LM75 */ tfinclude <lm75.h> Описание микросхемы LM75 (Рис. 5.21) можно найти на сайте производите- ля: http://www.national.com. Рис. 5.21. Микросхема LM75 — внешний вид (а) и назначение выводов (б). SDA С 1 SCL С 2 O.S. С 3 GNDQ 4 ]+Vs JAO □ А1 ЗА2
5.13. Функции шины I2C 315 Пример подключения LM75 к микроконтроллеру AT90S2313 показан на Рис. 5.22. Цепи питания, сброса и кварцевый резонатор не показаны (см. Рис. 4.1). Адрес устройства — 0, т. к. все выводы А0...А2 соединены с GND. DD1 AT90S2313 DD2 LM75 XTAL1 XTAL2 RESET PD0/RXD PD1/TXD PD2/INT0 PD3/INT1 РО4Д0 Р05Д1 PD6/ICP PB0/AIN0 PB1/AIN1 РВ2 PB3/OC1 РВ4 РВ5 РВ6 РВ7 12 13 77 16 18 OUT Рис. 5.22. Пример подключения LM75 к микроконтроллеру AT90S2313. Пример: /* Пример программы для доступа к LM75 */ /* Микроконтроллер AT90S2313, шина I2C LM75 подключена к PORTB. Для сигнала sda используем 0 бит порта, для scl - 1 бит порта (Рис. 5.22) */ ttinclude <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла lm75.h объявим порт, используемый для подключения шины I2C, и биты порта, используемые для сигналов sda и scl */ /* Шина I2C подключена к PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; */ #asm .equ _i2c_port=0xl8 .equ _sda_bit=0 .equ _scl_bit=l ttendasm /* Теперь можно подключать функции температурного датчика LM75 */ ttinclude <lm75.h> /* Основная функция программы */ void main(void) { int temper_lm75; // Объявляем переменную temper_lm75 /* инициализируем шину I2C */
316 Глава 5. Использование библиотечных функций i2c_init(); /* инициализируем LM75 - датчик с адресом 0 */ lm75_init(0,25,30,0); // Зададим thyst = 25‘С, tos=30’C, параметр pol = 0 /* цикл опроса температурного датчика */ while (1) { /* Считаем температуру датчика хЮ’С в переменную temper_lm75 */ temper_lm75=lm75_temperature_10(0); // далее в программе можно использовать // текущее значение температуры ) ) За более подробной информацией об использовании LM75 обратитесь к их описанию (datasheet) на сайте производителя. 5.13.2. Функции термометра/термостата DS1621 от Dallas Semiconductor Эти функции предназначены для облегчения пользователю сопряжения меж- ду программами Си и температурным датчиком DS1621 шины 12С. Перечень функций термометра/термостата DS1621 и их действия приведены в Табл. 5.26. Таблица 5.26, Перечень функций термометра/термостата DS1621 Функция Действие void ds!621_init (unsigned char chip, signed char tlow, signed char thigh, unsigned char pol) Эта функция инициализирует чип DS1621. Прежде чем вызывать эту функцию, должна быть инициализирована шина 12С вызовом функции i2c_init. Это первая функция, которая должна быть вызванадо использования других функций DS1621. Если к шине 12С подключено более одного чипа, то функция должна быть вызвана к каждому чипу, определив соответствующий функциональный параметр chip. К шине 12С можно подключить максимум 8 чипов DS1621, их chip-адрес может быть от 0 до 7. Адрес задаётся выводами А0...А2. Кроме измерения температуры DS1621 может работать как термостат. Выход Tout становится активным, когда температура превышает предел thigh, и выходит из активного состояния, когда температура падает ниже предела tlow. Как tlow, так и thigh выражены в °C. Параметр pol представляет полярность Tout выхода DS1621 в активном состоянии. Если pol — 0, активным является низкий уровень, и если pol — 1, активным является высокий уровень. Более подробно см. описание (datasheet) на DS1621 unsigned char ds!621_get_status (unsigned char chip) Эта функция читает содержимое регистра конфигурации/статуса DS1621 с адресом chip void dsl621_set_status (unsigned char chip, unsigned char data) Эта функция устанавливает содержимое регистра конфигурации/статуса DS1621 с адресом chip
5.13. Функции шины 12С 317 (продолжение) Функция Действие void ds 162 l_start (unsigned char chip) Эта функция выводит DS 1621 с адресом chip из режима пониженного потребления (power-down), и начинается измерение температуры и термостатирование void dsl621_stop (unsigned char chip) Эта функция вводит DS1621 с адресом chip в режим пониженного потребления (power-down) и останавливает измерение температуры и термостатирование int dsl621_temperature_10 (unsigned char chip) Эта функция возвращает температуру датчика DS1621 с адресом chip. Температура выражена в °C и умножена на 10 Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций размещаются в файле dsl621.h, расположенном в поддиректории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл dsl621.h. Прототипы функций шины 12С автомати- чески подключаются при подключении dsl621.h. До подключения файла dsl621.h необходимо объявить, какой порт микрокон- троллера и какие биты порта будут использованы для связи с DS1621 через шину 12С. Синтаксис: #asm .equ i 2 c_port=адрес_порта .equ sda_bit=6nT_nopTa_sda .equ sс1_bi t=бит_порта_з с1 tfendasm Адрес соответствующего порта можно найти в файле .h, который соответству- ет используемому микроконтроллеру. Файлы с расширением .h расположены в поддиректории ..\INC. Пример: /* Использование функций термометра/термостата DS1621 */ /* Микроконтроллер AT90S2313 */ tfinclude <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла dsl621.h объявим порт, используемый для подключения шины 12С */ /* Шина I2C подключена к PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; Для сигнала sda используем 0 бит порта, для scl - 1 бит порта */ #asm .equ __i2c_port=0xl8
318 Глава 5. Использование библиотечных функций .equ __sda_bit=O .equ __scl_bit=l #endasm /* Теперь можно подключать функции термометра/термостата DS1621 */ tfinclude <dsl621.h> Описание микросхемы DS1621 (Рис. 5.23) можно найти на сайте производи- теля: http://www.dalsemi.com/. SDA [ 1 SCL[ 2 Tout [ 3 GND[ 4 W 7 O> 6 S 5 ]Vdd ]A0 ]A1 JA2 a) 6) Puc. 5.23. Микросхема DS 1621 — внешний вид (а) и назначение выводов (б). Пример подключения DS1621 к микроконтроллеру AT90S2313 показан на Рис. 5.24. Цепи питания, сброса и кварцевый резонатор не показаны (см. Рис. 4.1). Адрес устройства — 0x001, т. к. вывод АО — 1 (соединён с Vcc), а А1 и А2 — 0 (соединены с GND). DD1 AT90S2313 XTAL1 XTAL2 RESET PD0/RXD PD1/TXD PD2/INT0 PD3/INT1 PD4/T0 PD5/T1 PD6/ICP PB0/AIN0 PB1/AIN1 РВ2 PB3/OC1 РВ4 РВ5 РВ6 РВ7 DD2 DS1621 SDA АО — SCL А1 Tout А2 R1 --->VCC ► OUT Рис. 5.24. Пример подключения DS1621 к микроконтроллеру AT90S2313. Пример: /* Пример программы для доступа к DS1621 */ /* Микроконтроллер AT90S2313, шина I2C DS1621 подключена к PORTB. Для сигнала sda используем 0 бит порта, для scl - 1 бит порта (Рис. 5.24) */ ttinclude <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла dsl621.h объявим порт, используемый
5.13. Функции шины 12С 319 для подключения шины I2C, и биты порта, используемые для сигналов sda и scl */ /* Шина I2C подключена к PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; */ #asm .equ _i2c_port=0xl8 .equ _sda_bit=0 .equ _scl_bit=l #endasm /* Теперь можно подключать функции термометра/термостата DS1621 */ #include <ds!621.h> /* Основная функция программы */ void main(void) { int temper_dsl621; // Объявляем переменную temper_dsl621 /* инициализируем шину 12С */ i2c_init(); /* инициализируем термометр/термостат DS1621 с адресом 1 */ dsl621_init(1,25,30,0);// Зададим tlow=25’C, thigh=30’C, // параметр pol=0 /* цикл опроса термометра/термостата DS1621 */ while (1) { /* читаем температуру термометра/термостата хЮ’С в переменную temper_dsl621 */ temper_dsl621=dsl621_temperature_10(1); // далее в программе можно использовать // текущее значение температуры ) ) За более подробной информацией об использовании DS1621 обратитесь к их описанию (datasheet) на сайте производителя. 5.13.3. Функции часов реального времени PCF8563 от Philips Эти функции предназначены для облегчения пользователю сопряжения меж- ду программами Си и часами реального времени (real time clock — RTC) PCF8563 шины I2C. Перечень функций часов реального времени PCF8563 и их действия приведе- ны в Табл. 5.27.
320 Глава 5. Использование библиотечных функций Таблица 5.27. Перечень функций часов реального времени PCF8563 Функция Действие void rtcjni t (unsigned char ctrll, unsigned char clkout, unsigned char timer_ctrl) Эта функция инициализирует чип PCF8563 unsigned char rtc_read (unsigned char address) Эта функция читает байт, хранящийся в регистре PCF8563 по адресу address void rtc_write (unsigned char address, unsigned char data) Эта функция сохраняет байт data в регистре PCF8583 по адресу address unsigned char rtc_get_time (unsigned char *hour, unsigned char *min, unsigned char *sec) Эта функция возвращает текущее время, измеренное RTC (Часами реального времени). Указатели *hour, *min и *sec должны указывать на переменные, которые должны получить значения часов, минут и секунд. Функция возвращает значение 1, если прочитанные значения правильные. Если функция возвращает 0, значит напряжение питания чипа упало ниже значениия Vlow и значение времени неправильное void rtc_set_time (unsigned char hour, unsigned char min, unsigned char sec) Эта функция устанавливает текущее время RTC. Параметры hour, min и sec представляют значения часов, минут и секунд void rtc_get_date (unsigned char *date, unsigned char *month, unsigned *year) Эта функция возвращает текущую дату, измеренную RTC. *date, *month и *уеаг указывают на переменные, которые должны получить значения дня, месяца и года void rtc_set_date (unsigned char date, unsigned char month, unsigned year) Эта функция устанавливает текущую дату RTC void rtc_alarm_off(void) Эта функция запрещает функцию RTC будильника void rtc_alarm_on(void) Эта функция разрешает функцию RTC будильника void rtc_get_alarm (unsigned char *date, unsigned char *hour, unsigned char *min) Эта функция возвращает время будильника и дату RTC. *date, *hour и *min указывают на переменные, которые должны получить значения даты, часов и минут void rtc_set_alarm (unsigned char date, unsigned char hour, unsigned char min) Эта функция устанавливает время будильника и дату RTC. Параметры date, hour и min представляют значения даты, часов и минут. Если date установлен в 0, то этот параметр будет проигнорирован. После вызова этой функции будильник будет выключен. Его можно разрешить, используя функцию rtc_alarm_on void rtc_set_timer (unsigned char val) Эта функция устанавливает значение для обратного отсчёта таймера PCF8563 Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций размещаются в файле pcf8563.h, расположенном в поддиректории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл pcf8563.h. Прототипы функций шины 12С автома- тически подключаются при подключении pcf8563.h.
5.13. Функции шины I2C 321 До подключения файла pcf8563.h необходимо объявить, какой порт микро- контроллера и какие биты порта будут использованы для связи с PCF8563 через шину 12С. Синтаксис: #asm .equ i2c_port=адрес_порта .equ s da_b i t=бит_порта_э da .equ __scl_bit=6nT_nopTa_scl #endasm Адрес соответствующего порта можно найти в файле .h, который соответству- ет используемому микроконтроллеру. Файлы с расширением .h расположены в поддиректории ..\INC. Пример: /* Использование функций часов реального времени PCF8563 */ /* Микроконтроллер AT90S2313 */ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла pcf8563.h объявим порт, используемый для подключения шины I2C */ /* Шина I2C подключена к PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; Для сигнала sda используем 0 бит порта, для scl — 1 бит порта */ #asm .equ __i2c_port=0xl8 .equ __sda_bit=0 .equ __scl_bit=l tfendasm /* Теперь можно подключать функции часов реального времени PCF8563 */ tfinclude <pcf8563.h> Описание микросхемы PCF8563 (Рис. 5.25) можно найти на сайте производи- теля: http://www.semiconductors.philips.com. OSCI [1 -о 8 ] VDD OSCOt 2 7JCLKOUT 1nt[ з “ 6 ]sel- ves [ 4 w 5 ] SDA б) Рис. 5.25. Микросхема PCF8563 — внешний вид (а) и назначение выводов (б).
322 Глава 5. Использование библиотечных функций Пример подключения PCF8563 к микроконтроллеру AT90S2313 показан на Рис. 5.26. Цепь сброса и кварцевый резонатор микроконтроллера, а также цепи питания микросхем не показаны (см. Рис. 4.1). DD2 AT90S2313 С1 5-25 pF Х1 32.768 kHz DD1 PCF8563 XTAL1 XTAL2 RESET PDO/RXD PD1/TXD PD2/INT0 PD3/INT1 PD4/T0 PD5/T1 PD6/ICP PBO/AINO PB1/AIN1 PB2 PB3/OC1 PB4 PB5 PB6 PB7 Рис. 5.26. Пример подключения PCF8563 к микроконтроллеру AT90S2313. Void rtc_init(unsigned char ctrl2, unsigned char clkout, unsigned char timer_ctrl) должна быть первой функцией, которая будет вызвана до использования других функций PCF8563. Прежде чем вызывать эту функцию, должна быть инициали- зирована шина 12С вызовом функции i2c_init. К шине 12С может быть подключён только один чип PCF8583. Параметр ctr!2 определяет значение инициализации для регистра Control/Status 2 (Регистр управления/состояния 2) PCF8563. Заголовочный файл pcf8563.h определяет следующие макросы, которые позволяют легко установить параметр ctr!2: • RTC_TIE_ON устанавливает бит TIE регистра Control/Status 2 в 1, разрешая прерывание таймера; • RTC_AIE_ON устанавливает бит AIE регистра Control/Status 2 в 1, разрешая прерывание будильника (Alarm); • RTC_TP_ON устанавливает бит TI/TP регистра Control/Status 2 в 1, разре- шая генерацию прерывания по таймеру постоянным активным уровнем. Если бит TI/TP установлен в 0, то прерывание по таймеру будет сгенериро- вано как импульсный сигнал на каждый период обратного счёта таймера. Эти макросы можно объединить, используя оператор |, для того чтобы уста- навливать сразу несколько битов в 1. Параметр clkout определяет значение инициализации для регистра CLKOUT Frequency (Тактовая частота) PCF8563. Заголовочный файл pcf8563.h определяет следующие макросы, которые позволяют легко установить параметр clkout: • RTC_CLKOUT_OFF запрещает генерацию импульсов на выходе CLKOUT PCF8563; • RTC_CLKOUT_1 генерирует импульсы 1 Гц на выходе CLKOUT PCF8563; • RTC_CLKOUT_32 генерирует импульсы 32 Гц на выходе CLKOUT PCF8563; • RTC_CLKOUT_1024 генерирует импульсы 1024 Гц на выходе CLKOUT PCF8563;
5.13. Функции шины 12С 323 • RTC_CLKOUT_32768 генерирует импульсы 32768 Гц на выходе CLKOUT PCF8563. Параметр timer ctrl определяет значение инициализации для регистра Timer Control (Управление таймером) PCF8563. Заголовочный файл pcf8563.h определяет следующие макросы, которые позволяют легко уста- новить параметр timer_ctrl: • RTC_TIMER_OFF запрещает обратный счёт таймеру PCF8563; • RTC_TIMER_CLK_l_60 устанавливает тактовую частоту обратного счёта таймера PCF8563, равную 1/60 Гц; • RTC TIMER CLK1 устанавливает тактовую частоту обратного счёта тай- мера PCF8563, равную 1 Гц; • RTC_TIMER_CLK_64 устанавливает тактовую частоту обратного счёта тай- мера PCF8563, равную 64 Гц; • RTC_TIMER_CLK_4096 устанавливает тактовую частоту обратного счёта таймера PCF8563, равную 4096 Гц. Более подробно см. описание (data sheet) на PCF8563. Пример: /* Пример программы для доступа к PCF8563 */ /* Микроконтроллер AT90S2313, шина I2C PCF8563 подключена к PORTD. Для сигнала sda используем 0 бит порта, для scl - 1 бит порта (Рис. 5.26) */ ^include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла pcf8563.h объявим порт, используемый для подключения шины I2C, и биты порта, используемые для сигналов sda и scl */ /* Шина I2C подключена к PORTD микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTD=Oxl2; */ #asm .equ __i2c_port=0xl2 .equ __sda_bit=0 .equ __scl_bit=l tfendasm /* Теперь можно подключать функции часов реального времени PCF8563 */ ^include <pcf8563.h> /* Основная функция программы */ void main(void) { unsigned char test; // Объявляем переменную test для // проверки правильности значений unsigned char hour,minute,second; // Объявляем переменные для // хранения значений часов, // минут и секунд
324 Глава 5. Использование библиотечных функций unsigned char date, month, year; // Объявляем переменные для // хранения значений даты, // месяца и года /* инициализируем шину I2C */ i2c_init(); /* инициализируем часы реального времени PCF8563, разрешаем прерывание будильника (Alarm), частота на выходе CLKOUT = 32768 Гц, запрещаем обратный счёт таймера */ rtc_init(RTC_AIE_ON,RTC_CLKOUT_32768,RTC_TIMER_OFF); test=rtc_get_time(&hour,&minute,&second);// читаем время из RTC /* Если test=l, то в переменных hour,minute,second содержатся правильные текущие значения часов, минут и секунд */ test=rtc_get_time(&date,&month,&year);// читаем дату из RTC /* Если test=l, то в переменных date, month, year содержатся правильные текущие значения даты, месяца и года */ // далее в программе можно использовать // текущее значение времени и даты } За более подробной информацией об использовании PCF8563 обратитесь к их описанию (datasheet) на сайте производителя. 5.13.4 . Функции часов реального времени PCF8583 от Philips Эти функции предназначены для облегчения пользователю сопряжения меж- ду программами Си и часами реального времени (real time clock — RTC) PCF8583 шины I2C. Перечень функций часов реального времени PCF8583 и их действия приведе- ны в Табл. 5.28. Таблица 5.28. Перечень функций часов реального времени PCF8583 Функция Действие Эта функция возвращает значение регистра Control/Status (Управления/состояния) PCF8583. Вызовом этой функции автоматически корректируются глобальные unsigned char rtc_get_status (unsigned char chip) переменные rtc_status и rtc_alarm. Переменная rtc_status содержит значение регистра Control/Status (Управления/состояния) PCF8583. Переменная rtc_alarm имеет значение 1, если будильник RTC сработал
5.13. Функции шины I2C 325 (продолжение) Функция Действие unsigned char rtc_read (unsigned char chip, unsigned char address) Эта функция читает байт, хранящийся в SRAM PCF8583 void rtc_init (unsigned char chip, unsigned char dated.alarm) Эта функция инициализирует чип PCF8583. Прежде чем вызывать эту функцию, шина 12С должна быть инициализирована вызовом функции i2c_init. Это — первая функция, которая должна быть вызвана до использования других функций PCF8583. Если к шине 12С подключено более одного чипа, то функция должна быть вызвана к каждому чипу, определив соответствующий функциональный параметр chip. К шине 12С можно подключить максимум 2 чипа PCF8583, их chip- адрес может быть 0 или 1. Параметр dated_alarm определяет, требуется ли для будильника RTC как время, так и дата (dated_alarm=l) или только время (dated_alarm=O). Более подробно см. описание (data sheet) на PCF8583. После вызова этой функции будильник RTC будет выключен void rtc_write (unsigned char chip, unsigned char address, unsigned char data) Эта функция сохраняет байт data в SRAM PCF8583. При записи в SRAM следует учитывать, что ячейки по адресам 10h и llh используются для хранения текущего значения года void rtc_get_time (unsigned char chip, unsigned char *hour, unsigned char *min, unsigned char *sec, unsigned char *hsec) Эта функция возвращает текущее время, измеренное RTC (Часами реального времени). Указатели *hour, *min, *sec и *hsec должны указывать на переменные, которые должны получить значения часов, минут, секунд и сотых долей секунд void rtc_get_alarm_time (unsigned char chip, unsigned char *hour, unsigned char *min, unsigned char *sec, unsigned char *hsec) Эта функция возвращает время будильника RTC. Указатели *hour, *min, *sec и *hsec должны указывать на переменные, которые должны получить значения часов, минут, секунд и сотых долей секунд void rtc_set_alarm_time (unsigned char chip, unsigned char hour, unsigned char min, unsigned char sec, unsigned char hsec) Эта функция устанавливает время будильника RTC. Параметры hour, min, sec и hsec представляют значения часов, минут, секунд и сотых долей секунд void rtc_get_alarm_date (unsigned char chip, unsigned char *date, unsigned char *month) Эта функция возвращает дату будильника RTC. Указатели *day и *month должны указывать на переменные, которые должны получить значения даты и месяца void rtc_set_alarm_date (unsigned char chip, unsigned char date, unsigned char month) Эта функция устанавливает дату будильника RTC Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций размещаются в файле pcf8583.h, расположенном в поддиректории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл pcf8583.h. Прототипы функций шины 12С автома- тически подключаются при подключении pcf8583.h. До подключения файла pcf8583.h необходимо объявить, какой порт микро- контроллера и какие биты порта будут использованы для связи с PCF8583 через шину 12С.
326 Глава 5. Использование библиотечных функций Синтаксис: #asm .equ __i2c_port=aflpec_nopTa .equ __sda_bit=6nT_nopTa_sda .equ __scl_bit=6nT_nopTa_scl #endasm Адрес соответствующего порта можно найти в файле .h, который соответству- ет используемому микроконтроллеру. Файлы с расширением .h расположены в поддиректории ..\INC. Пример: /* Использование функций часов реального времени PCF8583 */ /* Микроконтроллер AT90S2313 */ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла pcf8583.h объявим порт, используемый для подключения шины I2C */ /* Шина I2C подключена к PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; Для сигнала sda используем 0 бит порта, для scl — 1 бит порта */ #asm .equ __i2c_port=0xl8 .equ __sda_bit=0 .equ __scl_bit=l #endasm /* Теперь можно подключать функции часов реального времени PCF8583 */ tfinclude <pcf8583.h> Описание микросхемы PCF8583 (Рис. 5.27) можно найти на сайте производи- теля: http://www.semiconductors.philips.com. OSCI С 1 OSCO[2 АО t 3 VSS [ 4 JVDD ]ПМТ J SCL ] SDA 6) Puc. 5.27. Микросхема PCF8583 — внешний вид (а) и назначение выводов (6).
5.13. Функции шины 12С 327 Пример подключения PCF8583 к микроконтроллеру AT90S2313 показан на Рис. 5.28. Цепь сброса и кварцевый резонатор микроконтроллера, а также цепи питания микросхем не показаны (см. Рис. 4.1). Адрес устройства — 0, т. к. вывод АО соединён с GND. VCC < DD2 С1 5-25 пФ AT90S2313 Х1 32.768 кГц DD1 PCF8583 XTAL1 XTAL2 RESET PD0/RXD PD1/TXD PD2/INT0 PD3/INT1 PD4/T0 PD5/T1 PD6/ICP PB0/AIN0 PB1/AIN1 РВ2 PB3/OC1 РВ4 РВ5 РВ6 РВ7 Рис. 5.28. Пример подключения PCF8583 к микроконтроллеру AT90S2313. Пример: /* Пример программы для доступа к PCF8583 */ /* Микроконтроллер AT90S2313, шина I2C PCF8583 подключена к PORTD. Для сигнала sda используем 0 бит порта, для scl - 1 бит порта (Рис. 5.28) */ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла pcf8583.h объявим порт, используемый для подключения шины I2C, и биты порта, используемые для сигналов sda и scl */ /* Шина I2C подключена к PORTD микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTD=Oxl2; */ #asm .equ __i2c_port=0xl2 .equ __sda_bit=0 .equ __scl_bit=l tfendasm /* Теперь можно подключать функции часов реального времени PCF8583 */ #include <pcf8583.h> /* Основная функция программы */ void main(void) { unsigned char hour,minute,second,hsecond; // Объявляем переменные для // хранения значений часов,
328 Глава 5. Использование библиотечных функций unsigned char date, month; // минут, секунд и сотых // долей секунд // Объявляем переменные для хранения // значений даты и месяца /* инициализируем шину I2C */ i2c_init(); /* инициализируем часы реального времени PCF8583 с адресом О, будильник с датой */ rtc_init(0,1); rtc_get_time(0,&hour,&minute,&second,&hsecond);// читаем время //из RTC с // адресом 0 rtc_get_alarm_date(0,&date,&month); // читаем дату из будильника RTC с // адресом 0 // далее в программе можно использовать // текущее значение времени и даты } Более подробно см. описание (data sheet) на PCF8583. 5.13.5 . Функции часов реального времени DS1307 от Dallas Semiconductor Эти функции предназначены для облегчения пользователю сопряжения меж- ду программами Си и часами реального времени (real time clock — RTC) DS 1307 шины I2C. Перечень функций часов реального времени DS1307 и их действия приведены в Табл. 5.29. Таблица 5.29. Перечень функций часов реального времени DS1307 Функция Действие void rtc_get_time (unsigned char *hour, unsigned char *min, unsigned char *sec) Эта функция возвращает текущее время, измеренное RTC (Часами реального времени). Указатели *hour, *min и *sec должны указывать на переменные, которые должны получить значения часов, минут и секунд void rtc_set_time (unsigned char hour, unsigned char min, unsigned char sec) Эта функция устанавливает текущее время RTC (Часов реального времени). Параметры hour, min и sec представляют значения часов, минут и секунд void rtc_get_date (unsigned char *date, unsigned char *month, unsigned char *year) Эта функция возвращает текущую дату, измеренную RTC (Часами реального времени). *date, *month и *уеаг указывают на переменные, которые должны получить значения даты, месяца и года
5.13. Функции шины 12С 329 (продолжение) Функция Действие void rtc_set_date (unsigned char date, unsigned char month, unsigned char year) Эта функция устанавливает текущую дату RTC (Часов реального времени) void rtc_init (unsigned char rs, unsigned char sqwe, unsigned char out) Эта функция инициализирует чип DS1307. Это первая функция, которая должна быть вызвана до использования других функций DS1307. Прежде чем вызывать эту функцию, шина 12С должна быть инициализирована вызовом функции i2c_init. Параметр rs определяет значение частоты выходных прямоугольных импульсов на выводе SQW/OUT: • 0 для 1 Гц; • 1 для 4096 Гц; • 2для8192Гц; • 3 для 32768 Гц. Если параметр sqwe установлен в 1, то выход прямоугольных импульсов на выводе SQW/OUT разрешён. Параметр out определяет логический уровень на выводе SQW/OUT, когда выход прямоугольных импульсов запрещён (sqwe=0). Более подробно см. описание (data sheet) на DS1307 Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций размещаются в файле dsl307.h, расположенном в поддиректории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл dsl307.h. При этом автоматически подключаются прототипы функций шины 12С. До подключения файла dsl307.h необходимо объявить, какой порт микрокон- троллера и какие биты порта будут использованы для связи с DS1307 через шину 12С. Синтаксис: #asm .equ i2c_port=адрес_порта .equ __sda_bit=6nT_nopTa_sda .equ __scl_bit=6nT_nopTa_scl #endasm Адрес соответствующего порта можно найти в файле .h, который соответству- ет используемому микроконтроллеру. Файлы с расширением .h расположены в поддиректории ..\INC. Пример: /* использование функций часов реального времени DS1307 */ /* Микроконтроллер AT90S2313 */ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла ds1307.h объявим порт, используемый для подключения шины 12С */
330 Глава 5. Использование библиотечных функций /★ Шина I2C подключена к PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; Для сигнала sda используем 0 бит порта, для scl - 1 бит порта */ #asm .equ _i2c_port=0xl8 .equ _sda_bit=0 .equ _scl_bit=l #endasm /* Теперь можно подключать функции часов реального времени DS1307 */ #include <dsl307.h> Описание микросхемы DS1307 (Рис. 5.29) можно найти на сайте производи- теля: http://www.dalsemi.com/. ХИ Х2[ Vbat[ GND[ 3 Сл> 4 3 JVcc JSQW/OUT JSCL J SDA 6) Puc. 5.29. Микросхема DS1307 — внешний вид (а) и назначение выводов (б). Пример подключения DS1307 к микроконтроллеру AT90S2313 показан на Рис. 5.30. Цепь сброса и кварцевый резонатор микроконтроллера, а также цепи питания микросхем не показаны (см. Рис. 4.1). DD1 AT90S2313 DD2 DS1307 XTAL1 XTAL2 RESET PD0/RXD PD1/TXD PD2/INT0 PD3/INT1 PD4/T0 PD5/T1 PD6/ICP PB0/AIN0 PB1/AIN1 РВ2 PB3/OC1 РВ4 РВ5 РВ6 РВ7 Х1 32,768 кГц Рис. 5.30. Пример подключения DS1307 к микроконтроллеру AT90S2313. Пример: /* Пример программы для доступа к DS1307 */
5.13. Функции шины 12С 331 /* Микроконтроллер AT90S2313, шина I2C DS1307 подключена к PORTB. Для сигнала sda используем 0 бит порта, для scl - 1 бит порта (Рис. 5.30) */ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла ds1307.h объявим порт, используемый для подключения шины I2C, и биты порта, используемые для сигналов sda и scl */ /* Шина I2C подключена к PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; */ #asm .equ _i2c_port=0xl8 .equ _sda_bit=0 .equ _scl_bit=l #endasm /* Теперь можно подключать функции часов реального времени DS1307 */ #include <dsl307.h> /* Основная функция программы */ void main(void) { unsigned char hour,minute,second;// Объявляем переменные для // хранения значений часов, // минут и секунд unsigned char date, month,уear; // Объявляем переменные для // хранения значений даты, // месяца и года /* инициализируем шину I2C */ i2c_init(); /* инициализируем часы реального времени DS1307 Разрешим выход прямоугольных импульсов на выводе SQW/OUT; Частота на выходе SQW/OUT = 1 Гц */ rtc_init(0,1,0); rtc_get_time(&hour,&minute,^second);// читаем время из RTC DS1307 rtc_get_date(&date,&month,&year);// читаем дату из RTC DS1307 // далее в программе можно использовать // текущее значение времени и даты } За более подробной информацией об использовании DS1307 обратитесь к их описанию (datasheet) на сайте производителя.
332 Глава 5. Использование библиотечных функций 5.14. Функции часов реального времени DS1302 от Dallas Semiconductor Эти функции предназначены для облегчения пользователю сопряжения меж- ду программами Си и часами реального времени (real time clock — RTC) DS 1302. Перечень функций часов реального времени DS1302 и их действия приведены в Табл. 5.30. Таблица 5.30. Перечень функций часов реального времени DS1302 Функция Действие void rtc_init (unsigned char tc_on, unsigned char diodes, unsigned char res) Эта функция инициализирует чип DS1302. Это первая функция, которая должна быть вызвана до использования других функций DS1302. Если параметр tc_on установлен в 1, то разрешено функционирование DS1302 как зарядного устройства. Параметр diodes определяет количество используемых диодов, когда разрешена функция зарядного устройства. Этот параметр может иметь значения 1 или 2. Параметр res определяет значение резистора зарядки: • 0 — нет резистора; • 1 для резистора 2 кОм; • 2 для резистора 4 кОм; • 3 для резистора 8 кОм Более подробно см. описание (datasheet) на DS1302 unsigned char ds!302_read (unsigned char addr) Эта функция читает байт, хранящийся по адресу addr в регистрах или SRAM DS1302 void ds!302_write (unsigned char addr, unsigned char data) Эта функция загружает байт data по адресу addr в регистры или SRAM DS1302 void rtc_get_time (unsigned char *hour, unsigned char *min, unsigned char *sec) Эта функция возвращает текущее время, измеренное RTC (Часами реального времени). *hour, *min и *sec указывают на переменные, которые должны получить значения часов, минут и секунд Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций размещаются в файле dsl302.h, расположенном в поддиректории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл dsl302.h. До подключения файла dsl302.h необходимо объявить, какой порт микрокон- троллера и какие биты порта будут использованы для связи с DS1302. Синтаксис: #asm .equ _с1з1302_рогЬ=адрес_порта .equ _dsl302_io=6nT_nopTa_io .equ _dsl302_sclk=6nT_nopTa_sclk .equ _dsl302_rst=6nT_nopTa_rst #endasm
5.14. Функции часов реального времени DS1302 от Dallas Semiconductor 333 Адрес соответствующего порта можно найти в файле .h, который соответству- ет используемому микроконтроллеру. Файлы с расширением .h расположены в поддиректории ..\INC. Пример: /* Использование функций часов реального времени DS1302 */ /* Микроконтроллер AT90S2313 */ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла dsl302.h объявим порт, используемый для подключения микросхемы DS1302 */ /* Микросхему DS1302 подключим к PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; Для сигнала RST используем 0 бит порта, для 1/0-1 бит порта, для SCLK - 2 бит порта */ #asm .equ _dsl302_port=0xl8 .equ _dsl302_rst=0 .equ _dsl302_io=l .equ _dsl302_sclk=2 #endasm /* Теперь можно подключать функции часов реального времени DS1302 */ #include <dsl302.h> Описание микросхемы DS1302 (Рис. 5.31) можно найти на сайте производи- теля: http://www.dalsemi.com/. Vcc2 £ Х1С Х2 £ GNDf 1 2 3 4 СЛ к> 8 JVcci 7 3SCLK 6 J I/O 5 J RST б) W Рис, 5,31. Микросхема DS1302 — внешний вид (а) и назначение выводов (6). Пример подключения DS1302 к микроконтроллеру AT90S2313 показан на Рис. 5.32. Цепь сброса и кварцевый резонатор микроконтроллера, а также цепи питания микросхем не показаны (см. Рис. 4.1).
334 Глава 5. Использование библиотечных функций DD1 DD2 AT90S2313 DS1302 —XTAL1 XTAL2 «X RESET PBO/AINO PB1/AIN1 РВ2 РВЗ/0С1 РВ4 РВ5 РВ6 РВ7 Х1 32,768 кГц Рис. 5.32. Пример подключения DS1302 к микроконтроллеру AT90S2313. PDO/RXD PD1/TXD PD2/INT0 PD3/INT1 PD4/T0 PD5/T1 PD6/ICP Микросхема DS1302 может работать в качестве зарядного устройства. Упро- щённая схема на Рис. 5.33 показывает основные компоненты зарядного устройс- тва. Более подробно см. описание (data sheet) на DS1302. Бит 7 Бит 6 Бит 5 Бит 4 БитЗ Бит 2 Бит 1 Бит О Рис. 5.33. Упрощённая схема DS1302 как зарядного устройства. Пример: /* Пример программы для доступа к DS1302 */ /* Микроконтроллер AT90S2313, микросхема DS1302 подключена к PORTB. Для сигнала RST используем 0 бит порта, для 1/0-1 бит порта, для SCLK - 2 бит порта (Рис. 5.33) */ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR
5.15. LCD-функции 335 /* Перед подключением файла dsl302.h объявим порт, используемый для подключения микросхемы DS1302, и биты порта, используемые для сигналов RST, I/O и SCLK */ /* Микросхема DS1302 подключена к PORTB микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; */ #asm .equ ____dsl302_port=0xl8 .equ _dsl302_rst=0 .equ _dsl302_io=l .equ _dsl302_sclk=2 tfendasm /* Теперь можно подключать функции часов реального времени DS1302 */ #include <ds!302.h> /* Основная функция программы */ void main(void) { unsigned char hour,minute,second; // Объявляем переменные для // хранения значений часов, // минут и секунд rtc_init(1,2,1); // инициализируем часы реального времени DS1302 - // используем зарядное устройство с 2 диодами и // резистором 2 кОм rtc_get_time(&hour,&minute,^second); // читаем время из RTC DS1302 // далее в программе можно // использовать текущее значение // времени } За более подробной информацией об использовании DS1302 обратитесь к их описанию (datasheet) на сайте производителя. 5.15. LCD-функции LCD-функции предназначены для облегчения пользователю сопряжения между программами Си и алфавитно-цифровыми LCD-модулями со встроенным чипом HD44780 от Hitachi или эквивалентным. Контроллер HD44780 потенциально может управлять двумя строками по 40 символов в каждой (для модулей четырьмя строками по 40 символов используют- ся два однотипных контроллера). Название и описание выводов рассматриваемых LCD приведено в Табл. 5.31.
336 Глава 5. Использование библиотечных функций Таблица 5.31. Описание выводов LCD на базе HD44780 Название вывода Описание VSS Питание 0 V(-) VDD Питание +5 V (+) V0 Напряжение смещения, управляющее контрастностью RS Вход. Высокий уровень — данные; низкий — команды R/W Вход. Высокий уровень — чтение; низкий — запись Е Вход. Строб, сопровождающий сигналы на шине «Команды/Данные». LCD-модули с размером матрицы 4x40 символов имеют два вывода — Е1 и Е2, каждый для своей половины дисплея (для своего контроллера) DB0...DB7 Шина «Команды/данные» При помощи этих выводов LCD обменивается информацией с управляющим микроконтроллером (в нашем случае — с AVR). Микроконтроллер AVR посылает в LCD команды, управляющие режимами его работы, и коды выводимых симво- лов. В свою очередь LCD может посылать микроконтроллеру AVR по его запросу информацию о своём состоянии и данные из своих внутренних блоков памяти. Три вывода LCD предназначены для подачи питающего напряжения (VSS, VDD) и напряжения смещения (V0), которое управляет контрастностью дисплея. На Рис. 5.34 показана рекомендуемая схема питания этих выводов. LCD-модуль R = 10 - 20 кОм Рис. 5.34. Рекомендуемая схема питания LCD. Выводы DB0...DB7 используются для организации мультиплексированной шины «Команды/данные». На выводы RS, R/W, Е (или El, Е2 для LCD 4x40) микроконтроллер AVR выставляет управляющие сигналы. При помощи сигнала на линии RS микроконтроллер сообщает контроллеру LCD о том, что именно передаётся по шине: команда или данные. Если RS = 0, адресуется регистр команд, если RS = 1 — регистр данных. Данные через регистр данных, в зависимости от текущего режима, могут по- мещаться (или прочитываться) в видеопамять (DDRAM) или в ОЗУ знакогенера- тора (CGRAM) по текущему адресу. Информация, попадающая в регистр команд, интерпретируется устройством выполнения команд как управляющая последовательность. Прочтение регистра команд возвращает в семи младших битах текущее значение счётчика адреса (АС), а в старшем разряде — флаг занятости (BE).
5.75. LCD-функции 337 Сигнал на линии Е (или El, Е2) является стробом, сопровождающим сигналы на ши- не «Команды/данные». Запись информации в LCD происходит по спаду этого сигнала. Потенциал на управляющем выводе R/W задаёт направление передачи дан- ных: запись в RAM LCD (R/W = 0) или считывание оттуда (R/W = 1). Для случая, когда микроконтроллер имеет ограниченное количество линий ввода/вывода, предусмотрен второй вариант подключения LCD с использовани- ем 4-битной шины «Команды/данные». При этом каждый байт данных передаёт- ся по линиям DB4...DB7 последовательно двумя тетрадами, начиная со старшей. Контроллер LCD после приёма байта команды или байта данных требует не- которого времени для обработки полученной информации, в течение которого микроконтроллер AVR не должен выполнять новые передачи. Для того чтобы определить, когда контроллер LCD закончит свои внутренние операции, микроконтроллер AVR может опрашивать BUSY-флаг (флаг занятости — BE), который сбросится только тогда, когда контроллер LCD освободится. Второй, более простой способ заключается в том, что управляющий микроконтроллер, зная, сколько времени требуется LCD на обработку той или иной команды, просто вы- полняет временную задержку после каждой передачи информации. Видеопамять (DDRAM), имеющая общий объём 80 Б, предназначена для хра- нения кодов символов, отображаемых на LCD. Видеопамять организована в две строки по 40 символов в каждой. Эта привязка является жесткой и не подлежит изменению, другими словами, независимо от того, сколько реальных строк будет иметь каждый конкретный LCD-модуль, скажем, 80x1 или 20x4, адресация видеопамяти всегда производится как к двум строкам по 40 символов. При записи или считывании буфера данных обращение осуществляется к ячейке, на которую в данный момент указывает курсор. У двустрочных LCD пер- вые 40 ячеек буфера данных обычно отображаются на верхней строке дисплея, а вторые 40 ячеек — на нижней. Кроме DDRAM, контроллер LCD содержит ещё один блок памяти — знакоге- нератор. Его «прошивка», т. е. соответствие кодов начертанию символов, обычно имеется в описании LCD. Пример такой «прошивки» представлен в Табл. 5.32. Из допустимых для размещения в DDRAM кодов символы с кодами 0x00...0x07 (и их дубликаты с кодами 0x08...0x0F) имеют специальное назначение — это переопределяемые символы, графическое изображение кото- рых может назначить сам пользователь, поместив соответствующую информа- цию в области CGRAM. Для программирования доступны 8 переопределяемых символов в режиме с матрицей 5x7 точек. Для каждого из восьми перепрограмми- руемых символов в CGRAM отводится по 8 ячеек памяти, каждая из которых со- ответствует одной строке точек в изображении символа. Для символа 0 (код сим- вола 0x00) адреса ячеек памяти — 0x00...0x07, для символа 1 (код символа 0x01) адреса ячеек памяти — 0x08...0x0F и т. д. Таким образом, перепрограммируемая часть знакогенератора содержит 64 байта памяти (8x8). Для кодирования матрицы используются горизонтально «уложенные» байты, пять младших битов которых несут информацию о рисунке (причём 1 означает, что сегмент будет включён), 4-й бит каждого из 8 байтов матрицы определяет ле- вую колонку символа, а 0-й — правую. Старшие три бита не используются и могут иметь любые значения.
338 Глава 5. Использование библиотечных функций Таблица 5.32. Пример прошивки знакогенератора LCD-модуля на базе HD44780 Старшие4 бита(О4...О7) 0 1 2 3 4 5 6 7 8 9 A в с D Е р 0 CG RAM (0) 0 P F Ш w ч а ч 1 CG RAM (1) i 1 cr Q •3 I! я ш II и я 2 CG RAM (2) II aL. В R b f Ё ь ъ и щ ч 3 CG RAM (3) # 3 c S c в ы 11 • • д а 4 CG RAM (4) $ 4 D I d ii 3 i ь • • i ш со 5 CG RAM (5) B,« ••’и 5 E В u и р э X и Q 6 CG RAM (6) & 6 F u f I..I Й ж W щ +1 о Q 7 CG RAM (7) о G bJ g w •fl ..... я I • со I- S 8 CG RAM (0) n 8 H I..I ft h П и п • • VLJ ф 9 CG RAM (1) i Й I 1 1 ж i у lvl й >> •W *'1‘ S 3 г—Г А CG RAM (2) i J —n j A.. Ф К U U i 1 СО В CG RAM (3) + •Й-: K1 £ k ID 4 41 у 7 К ’т* * С CG RAM (4) ft? Ci i i 1 IS Ш М Я У. а D CG RAM (5) —* M ] m IS ъ н С н 1 1*1 Е CG RAM (6) W: N n ** Ы п £ h.. ‘fl F CG RAM (7) 0 — о £ 3 т £. •ft 1 1 Пример кодирования CGRAM для одного символа приведён на Рис. 5.35 (символ 2, код символа 0x02). Адрес строки сим вола 2 (код символа0х02) Данные строки символа 2 (код сим вол а 0x02) 1-я строкаОхЮ (16) 2-я строка0х11(17) 3-я строка0х12(18) 4- я строка0х13(19) 5-я строка0х14(20) 6-я строка0х15(21) 7-я строка0х16 (22) 8-я строка0х17(23) 4 3 2 1 0 (биты) ОЬ00001110=0х0Е 0b00010001 =0x11 0600011011=0x1 В 0600010101=0x15 0600010001=0x11 0600010101=0x15 0600001110=0х0Е 0600000000=0x00 Рис. 5.35. Пример кодирования символа (незначащие биты показаны светлым шрифтом).
5.75. LCD-функции 339 . Обратите внимание, что матрица программируемых символов допускает использование полной высоты строк (8 строчек для режима 5x7), но де- лать этого не рекомендуется, т. к. самая нижняя строчка является об- ластью подчеркивающего курсора. У контроллера HD44780 существует набор внутренних флагов (Табл. 5.33), оп- ределяющих режимы работы различных элементов контроллера. Таблица 5.33. Флаги, управляющие работой контроллера HD44780 Флаг Описание I/D Определяет режим смещения счётчика адреса АС, 0 — уменьшение, 1 — увеличение S Определяет режим сдвига содержимого экрана. 0 — сдвиг экрана не производится, 1 — после записи в буфер данных (DDRAM) очередного кода экран сдвигается в направлении, определяемом флагом I/D: 0 — вправо, 1 — влево. При сдвиге не производится изменение содержимого DDRAM. Изменяются только внутренние указатели расположения видимого начала строки в DDRAM S/C Флаг-команда, производящий вместе с флагом R/L операцию сдвига содержимого экрана (так же, как и в предыдущем случае, без изменений в DDRAM) или курсора. Определяет объект смещения: 0 — сдвигается курсор, 1 — сдвигается экран R/L Флаг-команда, производящий вместе с флагом S/С операцию сдвига экрана или курсора. Уточняет направление сдвига: 0 — влево, 1 — вправо D/L Определяет ширину шины данных: 0...4 бита, 1 ...8 битов N Определяет режим развёртки изображения на LCD: 0 — одна строка, 1 — две строки F Определяет размер матрицы символов: 0...5 х 8 точек, 1...5 х 10 точек D Определяет наличие изображения: 0 — выключено, 1 — включено С Определяет подчёркивающий курсор: 0 — выключен, 1 — включён В Определяет курсор в виде мерцающего знакоместа: 0 — выключен, 1 — включён В Табл. 5.34 приведены значения управляющих флагов непосредственно пос- ле подачи на LCD напряжения питания. Переопределение значений флагов про- изводится специальными командами, записываемыми в регистр команд, при этом комбинации старших битов определяют группу флагов или команду, а млад- шие содержат собственно флаги. Таблица 5.34. Значения управляющих флагов после подачи напряжения питания Значение флага Описание I/D = 1 Режим увеличения счётчика на 1 S = 0 Без сдвига изображения D/L= 1 8-битная шина данных N = 0 Режим развертки одной строки F = 0 Символы с матрицей 5x8 точек D = 0 Отображение выключено C = 0 Курсор в виде символа подчёркивания выключен B = 0 Курсор в виде мерцающего знакоместа выключен
340 Глава 5. Использование библиотечных функций Список управляющих комбинаций битов регистра команд и выполняемые команды приведены в Табл. 5.35. Таблица 5.35. Управляющие комбинации битов регистра команд Управляющие биты (код) Описание команды DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 0 0 0 0 0 0 0 1 Очистить экран и установить курсор в нулевую позицию (счётчик адреса АС = 0), адресация АС на регистр данных (DDRAM) 0 0 0 0 0 0 1 — Установить курсор в нулевую позицию (АС = 0). Установить дисплей относительно DDRAM в начальную позицию. Содержимое DDRAM при этом не меняется 0 0 0 0 0 1 I/D S Установить направление сдвига курсора вправо (I/D = 1) или влево (I/D = 0) при записи/чтении очередного кода в DDRAM. Разрешить (S = 1) или запретить (S = 0) сдвиг экрана вместе со сдвигом курсора 0 0 0 0 1 D С В Включить (D = 1) или выключить (D = 0) экран. Зажечь (С=1) или погасить (С=0) курсор. Изображение курсора сделать мигающим (В = 1) или нет (В = 0) 0 0 0 1 S/C R/L — — Переместить курсор (S/C = 0) или сдвинуть экран (S/C = 1) вправо (R/L = 1) или влево (R/L = 0) 0 0 1 D/L N F — — Установить битность шины данных 4 бита (D/L = 0) или 8 битов (D/L = 1); количество строк дисплея — одна (N = 0) или две (N = 1); размер матрицы символов — 5x7 точек (F = 0) или 5 х 10 точек (F = 1) 0 1 Адрес CGRAM Установка адреса ОЗУ знакогенератора (CGRAM). После этой команды данные будут записываться/считываться в/из CGRAM X X X X X X 1 Адрес DDRAM Установка адреса DDRAM. После этой команды данные будут записываться/считываться в/из DDRAM X X X X X X X Так как на момент включения LCD ничего не отображает (флаг В = 0), то, для того чтобы вывести какой-либо текст, необходимо, как минимум, включить отоб- ражение, установив флаг В = 1. Вот пример широко распространённой последо- вательности команд (см. Табл. 5.35) для инициализации LCD: 0x38, ОхОС, 0x06 (знак «Ох» перед числом указывает на шестнадцатеричное основание). 0x38 уста- навливает режим отображения 2 строк с матрицей 5x8 точек и работу с 8-битной шиной данных; ОхОС включает отображение на экране LCD без отображения
5.15. LCD-функции 341 курсора, 0x06 устанавливает режим автоматического перемещения курсора слева направо после вывода каждого символа. Вывод на экран символа производится записью его кода в регистр данных. При этом символ размещается в DDRAM по текущему адресу, указываемому счёт- чиком адреса (АС) контроллера LCD, а значение АС увеличивается или уменьша- ется на 1. Чтобы произвести переустановку курсора на нужную позицию, необхо- димо присвоить АС соответствующее значение (см. Табл. 5.35). Здесь есть одна тонкость. Когда производится последовательная запись символов и в результате заполняется вся строка, курсор автоматически переходит на вторую строку; но если необходимо принудительно установить курсор, скажем, на начало второй строки, то будет неверным присвоить АС, казалось бы, логичное значение 0x28 (40), правильным является значение 0x40 (64). Значения адресов DDRAM в диа- пазоне 0x28...0x3F (а равно и 0x68...0x7F) являются неопределёнными, и резуль- таты работы с ними могут быть непредсказуемыми. Следует также отметить особенность вывода на LCD русского текста. Прежде всего знакогенератор LCD должен иметь русские буквы (см. Табл. 5.32). Кроме того, строку, предназначенную для вывода на LCD, содержащую английский текст и цифры, в программе Си можно записать несколькими способами, т. к. их коды Windows и коды английского знакогенератора LCD-дисплея совпадают. Пример: /* Эквивалентная запись определения строки «Text», содержащей английские буквы (см. Табл. 5.32)*/ char string[] = «Text»; char string[] = { 'T', 'e', ' x', 't', '\0' }; char stringf] = {0x54, 0x65, 0x78, 0x74, 0x00}; С русским текстом дело обстоит иначе. Дело в том, что сходные с английски- ми русские буквы берутся из английского знакогенератора, а остальное — из усе- чённого русского (см. Табл. 5.32). Кроме того, коды Windows русских букв НЕ СОВПАДАЮТ с их кодами знакогенератора LCD-дисплея. Поэтому строку, предназначенную для вывода на LCD, содержащую русский текст, в программе Си можно записать только одним способом, а именно перечислив коды знакоге- нератора соответствующих букв. Пример: /* Запись определения строки "Текст", содержащей русские буквы (см. Табл. 5.32) */ char stringf] = {0x54, 0x65, ОхВА, 0x63, OxBF, 0x00}; Напомним, что каждая строка должна заканчиваться нулевым символом, т. е. последнее значение 0x00 является символом конца строки, а не кодом выводимо- го символа. Для облегчения написания программ Си можно порекомендовать програм- му-перекодировщик Win-текста сообщений для LCD-контроллера HD44780, ко- торая так и называется «HD44780». Разработал её Леонид Егоров, e-mail: egorov@elsila.spb.ru.
342 Глава 5. Использование библиотечных функций Внешний вид окна программы представлен на Рис. 5.36. Рис. 5.36. Внешний вид программы «HD44780». В окно сообщений следует ввести требуемый текст и затем щёлкнуть по кноп- ке Создать Си-код. После этого в буфере обмена будет содержаться готовая строка с соответствующими кодами знакогенератора. Эту строку можно вставить в раз- рабатываемую программу Си. Только следует убедиться, что прошивка знакоге- нератора используемого LCD-дисплея соответствует прошивке, приведённой в Табл. 5.32. Подробнее см. описание (datasheet) на используемый LCD. Следует также отметить, что CodeVisionAVR версии 1.24.1х имеет специаль- ную директиву #pragma ruslcd, которая включает или выключает автоматическую перекодировку символов кириллицы при их выводе на LCD-модуль (см. Дирек- тива ttpragma ruslcd), что значительно упрощает вывод на LCD русского текста. 5.15.1. LCD-функции для дисплеев до 2x40 символов Эти LCD-функции предназначены для облегчения пользователю сопряжения между программами Си и алфавитно-цифровыми LCD-модулями с матрицей до 2x40 символов, со встроенным чипом HD44780 от Hitachi или эквивалентным. Перечень LCD-функций для дисплеев до 2x40 символов и их действия приве- дены в Табл. 5.36. Таблица 5.36. LCD-функции для дисплеев до 2x40 символов Функция Действие LCD-функции низкого уровня void _lcd_ready(void) Эта функция ждет, пока модуль LCD не будет готов принять данные. Эта функция должна быть вызвана до записи данных в LCD функцией _lcd_write_data
5.75. LCD-функции 343 (продолжение) Функция Действие void Jcd_write_data (unsigned char data) Эта функция записывает байт data в регистр инструкций LCD. Эта функция может быть использована для изменения конфигурации LCD void lcd_write_byte (unsigned char addr, unsigned char data) Эта функция записывает байт data в ОЗУ знакогенератора (CGRAM) или в DDRAM LCD. Переменная addr представляет собой код команды для установки адреса ячейки памяти ОЗУ знакогенератора (CGRAM) или DDRAM LCD (см. Табл. 5.35). Её значение для адресации CGRAM: addr=0x01 ХХХХХХ, где ХХХХХХ — адрес ячейки CGRAM, в которую следует записать байт data. Для адресации DDRAM: addr=0xl ХХХХХХХ, где ХХХХХХХ — адрес ячейки DDRAM, в которую следует записать байт data unsigned char lcd_read_byte (unsigned char addr) Эта функция читает байт из ОЗУ знакогенератора (CGRAM) или из DDRAM LCD. Переменная addr представляет собой код команды для установки адреса ячейки памяти ОЗУ знакогенератора (CGRAM) или DDRAM LCD (см. Табл. 5.35). Её значение для адресации CGRAM: addr=0x01 ХХХХХХ, где ХХХХХХ — адрес ячейки CGRAM, из которой следует прочитать байт. Для адресации DDRAM: addr=0xlХХХХХХХ, где ХХХХХХХ — адрес ячейки DDRAM, из которой следует прочитать байт LCD-функции высокого уровня void ledjnit (unsigned char lcd_columns) Эта функция инициализирует модуль LCD, очищает дисплей и устанавливает позицию для вывода символа в ряд 0 столбца 0. Должно быть определено количество столбцов LCD (lcd_colunms). Курсор не отображается. Эта функция должна быть вызвана первой, до использования других LCD-функций высокого уровня void lcd_clear(void) Эта функция очищает LCD и устанавливает позицию для вывода символа в ряд 0 столбца 0 void lcd_gotoxy (unsigned char x, unsigned char y) Эта функция устанавливает текущую позицию дисплея в столбец х и ряду. Нумерация рядов и столбцов начинается с 0 void lcd_putchar(char c) Эта функция отображает символ с в текущей дисплейной позиции void lcd_puts(char *str) Эта функция отображает в текущей дисплейной позиции строку str, расположенную в SRAM void lcd_putsf(char flash *str) Эта функция отображает в текущей дисплейной позиции строку str, расположенную во FLASH Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций определены в файле led.h, расположенном в подди- ректории ..\INC. Перед использованием этих функций директивой #include дол- жен быть подключён файл led.h., До подключения файла led.h необходимо объявить, какой порт микроконт- роллера будет использован для связи с модулем LCD.
344 Глава 5. Использование библиотечных функций Синтаксис: #asm .equ _1сЬ_рогС=адрес_порта ttendasm Адрес соответствующего порта можно найти в файле .h, который соответству- ет используемому микроконтроллеру. Файлы с расширением .h расположены в поддиректории ..\INC. В файле led.h поддерживаются следующие форматы LCD: 1x8, 2x12, 3x12, 1x16, 2x16 (Рис. 5.37), 2x20, 4x20, 2x24 и 2x40 символов. Пример: /* Использование LCD-функций для дисплеев до 2x40 символов */ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла led.h объявим порт, используемый для подключения модуля LCD */ /* Используем порт В микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; */ #asm .equ __lcd_port=0xl8 tfendasm /* Теперь перед использованием LCD-функций до 2x40 символов подключим заголовочный tfinclude <lcd.h> для дисплеев файл led.h */ Описание используемого LCD можно найти на сайте производителя (напри- мер, на сайтах фирм МЭЛТ http://www.melt.aha.ru/ или Data Vision http://www.datavision.com.tw/). LCD1 til til IIIIIIII a) 6) Puc. 5.37. LCD 1x16 символов — внешний вид (а) и назначение выводов (б). LCD-модуль должен быть подключён к битам порта в соответствии с Табл. 5.37.
5.15. LCD-функции 345 Таблица 5.37. Подключение модуля LCD к порту микроконтроллера Вывод LCD (сигнал) Бит порта микроконтроллера Вывод 4 (сигнал RS) БитО Вывод 5 (сигнал R/W) Бит 1 Вывод 6 (сигнал Е) Бит 2 Вывод 11 (сигнал DB4) Бит 4 Вывод 12 (сигнал DB5) Бит 5 Вывод 13 (сигнал DB6) Бит 6 Вывод 14 (сигнал DB7) Бит 7 Разумеется, согласно описанию на LCD, необходимо подключить питание LCD и напряжение для управления контрастностью. Пример подключения LCD к Порту В микроконтроллера AT90S2313 показан на Рис. 5.38. Цепь сброса, питания и кварцевый резонатор микроконтроллера не показаны (см. Рис. 4.1). DD1 AT90S2313 PD0/RXD PD1/TXD PD2/1NT0 PD3/INT1 PD4/T0 РО5Д1 PD6/ICP XTAL1 XTAL2 RESET PB0/AIN0 PB1/AIN1 РВ2 РВЗ/ОС1 РВ4 РВ5 РВ6 РВ7 VCC Рис. 5.38. Пример подключения LCD 2x16 к Порту В микроконтроллера AT90S2313. Пример: /* Использование LCD-функций низкого уровня для дисплеев до 2x40 символов */ /* Используется LCD 2x16, микроконтроллер AT90S2313. Схема подключения см. Рис. 5.38. */ /* Выведем изображение курсора в виде мигающего знакоместа в нулевую позицию дисплея */ ^include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR
346 Глава 5. Использование библиотечных функций /★ Перед подключением файла led.h объявим порт, используемый для подключения модуля LCD */ /* Используем Порт В микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; */ #asm .equ __lcd_port=0xl8 ftendasm /* Теперь перед использованием LCD-функций для дисплеев до 2x40 символов подключим заголовочный файл led.h */ ^include <lcd.h> /* Основная функция программы */ void main(void) { _lcd_ready(); // ждём, пока модуль LCD не будет готов // принять данные lcd_write_data(0x01); // очищаем экран lcd_write_data(0x02); // устанавливаем курсор в нулевую позицию // дисплея lcd_write_data(0x0f); // Включаем экран, включаем курсор, while (1); // изображение курсора делаем в виде мигающего // знакоместа (см. Табл. 5.33...5.35) // заканчиваем программу бесконечным циклом Пример: /* Использование LCD-функций для дисплеев до 2x40 символов */ /* Используется LCD 2x16, микроконтроллер AT90S2313. Схема подключения см. Рис. 5.38. */ /* Определим символ пользователя и выведем его изображение на LCD */ ^include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла led.h объявим порт, используемый для подключения модуля LCD */ /* Используем Порт В микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; */ #asm .equ __lcd_port=0xl8 #endasm /* Теперь перед использованием LCD-функций для дисплеев до 2x40 символов подключим заголовочный файл led.h */
5.75. LCD-функции 347 tfinclude <lcd.h> /* Определим тип данных byte byte==unsigned char */ typedef unsigned char byte; /* Определим номер символа. Для примера определим символ с номером 2, т. е. он будет располагаться в ОЗУ знакогенератора (CGRAM) и его код будет 0x02 (см. Табл. 5.32). Всего можно определить 8 символов 0...7 с кодами 0x00...0x07 */ byte number_char=2; /* Таблицу (массив) для определения символа пользователя (см. Рис. 5.35) для примера разместим во FLASH-памяти микроконтроллера (хотя можно и в SRAM) На расположение во FLASH указывает ключевое слово flash */ flash byte char2[8]= {ObOOOOlllO, ObOOOlOOOl, ObOOOllOll, ObOOOlOlOl, ObOOOlOOOl, ObOOOlOlOl, ObOOOOlllO, ObOOOOOOOO}; /* Основная функция программы */ void main(void) { byte i; // определим переменную i, используемую // как индекс byte command_addr; // определим переменную command_addr, // для хранения кода команды установки // адреса ячейки памяти ОЗУ знакогенератора // (CGRAM) (см. Табл. 5.35) /* инициализируем LCD 2x16 */ lcd_init(16); /* Вычислим значение кода команды установки адреса самого младшего байта символа 2 в CGRAM Так как под каждый символ отводится 8 Б, то байты 0-го символа имеют адреса 0х00...0х07, байты 1-го - 0x08...0x0F, ... байты п-го — (п*8) - (п*8+7) То есть, чтобы получить адрес самого младшего байта символа, надо номер символа (нумерация начинается с 0) умножить на 8 Чтобы получить код команды установки адреса в CGRAM, следует установить бит 6 в 1, а 7 — в 0. Это можно сделать, прибавив к адресу самого младшего байта символа число ОЬОЮООООО, т. к. для указания адреса ячеек CGRAM отводится 6
348 Глава 5. Использование библиотечных функций младших битов */ command_addr=(number_char*8)+0601000000; /* Перепишем значения таблицы для определения символа пользователя (char2[8]) в ячейки символа 2 CGRAM */ for (i=0; i<8; i++) // цикл for (см. Оператор for) { /* Тело цикла for */ lcd_write_byte(command_addr++,char2[i]); } /* Установим курсор в начальную позицию LCD */ lcd_gotoxy(0,0); /* Выведем строку */ lcd_putsf("Symbol number 2 of User: "); /* Отобразим определённый ранее символ 2 пользователя */ lcd_putchar(2); // 2 - это код символа в CGRAM // Можно записать 0x02 (см. Табл. 5.32) /* Обратите внимание, что этот символ дублируется еще и кодом ОхОА, т. е. символ также можно вывести командой: lcd_putchar(ОхОА); */ /* Завершим программу бесконечным циклом */ while (1); } // закрывающая скобка функции main Результат выполнения этой программы представлен на Рис. 5.39. Symbol nunber 2 of' User’ 8 S о И (Л 5 >>> осссш Sqooqood Рис. 5.39. Вывод символа 2, определённого пользователем. Обратите внимание, что если в строке, которая выводится на LCD, сим- волов больше, чем в строке LCD, то вывод продолжается на следующей строке.
5.15. LCD-функции 349 Пример: /* Использование LCD-функций для дисплеев до 2x40 символов */ /* Используется LCD 2x16 с русифицированным знакогенератором, микроконтроллер AT90S2313. Схема подключения см. Рис. 5.38. */ /* Эту программу можно использовать для просмотра прошивки знакогенератора LCD */ /* В верхней строке LCD с задержкой в 1 сек будут выводиться коды символов от 0 до 255, а в нижней строке - изображение символа с этим кодом Всё это циклически повторяется */ ^include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла led.h объявим порт, используемый для подключения модуля LCD */ /* Используем Порт В микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; */ #asm .equ _lcd_port=0xl8 ftendasm /* Теперь перед использованием LCD-функций для дисплеев до 2x40 символов подключим заголовочный файл led.h */ #include <lcd.h> /* подключим функции ввода/вывода */ ^include <stdio.h> /* подключим функции задержки */ #include <delay.h> char string_LCD[20]; // определим строку для вывода на LCD, т. е. // зарезервируем место в SRAM под массив // из 20 символов /* Основная функция программы */ void main(void) { unsigned int code_symbol; /* Инициализируем LCD 2x16 */ lcd_init(16) ; while (1) // бесконечный цикл while { /* Тело цикла while (см. Оператор while) */ /* Оператор цикла for (см. Оператор for) */
350 Глава 5. Использование библиотечных функций for (code_symbol=0x00; code_symbol<=255; code_symbol++) { /* Очистим LCD и установим курсор в ряд 0 столбца 0 */ lcd_clear(); /* Сформируем строку string_LCD для вывода на LCD (см. Стандартные функции ввода/вывода) */ #pragma ruslcd+ // Включим автоматическую перекодировку // символов кириллицы sprintf(string_LCD,"Код: 0х%02х", code_symbol); #pragma ruslcd- // Выключим автоматическую перекодировку // символов кириллицы /* Функция sprintf осуществляет форматные преобразования и помещает результат в строку string_LCD Спецификация преобразования "Код: 0х%02х\п" означает, что сначала будет выведена последовательность "Код: Ох", затем значение переменной code_symbol в шестнадцатеричном виде символами нижнего регистра (символ преобразования 'х'), причём будет выведено, по крайней мере, 2 символа. Если результат имеет менее двух символов, то слева он заполнится нулями (описатель width '02') */ /* Выведем строку string_LCD на LCD в позицию курсора */ lcd_puts(string_LCD); /* Установим курсор в начало второй строки LCD */ lcd_gotoxy(0,1); /* Аналогичным способом можно было вывести и вторую строку: sprintf(string_LCD, "Символ: %с",code_symbol); lcd_puts(string_LCD); но чтобы показать использование других LCD-функций для дисплеев до 2x40 символов, сделаем вывод второй строки другим способом */ /* Вывод второй строки на LCD в позицию курсора */ #pragma ruslcd+ // Включим автоматическую перекодировку // символов кириллицы 1cd_puts f("Символ: ") ; ftpragma ruslcd- // Выключим автоматическую перекодировку // символов кириллицы /* Выведем в текущую позицию курсора строку "Символ" Обратите внимание, что здесь нужно использовать функцию lcd_putsf, а не lcd_puts, т. к. при такой записи строка "Symbol" располагается во FLASH-памяти, а не в SRAM */ /* Установим курсор в позицию 10 второй строки LCD (счёт позиций начинается с 0) */ lcd_gotoxy(10,1); /* Выведем в текущую позицию курсора символ, код которого
5.15. LCD-функции 351 соответствует значению code_symbol */ lcd_putchar(code_symbol); /* Сделаем задержку в 1 сек */ delay_ms(1000); }; code_symbol=0x00; }; // закрывающая скобка для цикла for // обнулим значение code_symbol и всё повторим // закрывающая скобка для цикла while // закрывающая скобка для функции main Результат выполнения этой программы представлен на Рис. 5.40. Рис. 5.40. Вывод кода и изображения символа знакогенератора. Пример: /* Использование LCD-функций для дисплеев до 2x40 символов */ /* Используется LCD 2x16, микроконтроллер AT90S2313. Схема подключения см. Рис. 5.38. */ /* Демонстрируется использование функций записи в DDRAM LCD */ /* Демонстрируется вывод текста, содержащего русские буквы */ tfinclude <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла led.h объявим порт, используемый для подключения модуля LCD */ /* Используем Порт В микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; */ #asm .equ _lcd_port=0xl8 #endasm /* Теперь перед использованием LCD-функций для дисплеев до 2x40 символов подключим заголовочный файл led.h */ #include <lcd.h> /* Определим тип данных byte byte==unsigned char */ typedef unsigned char byte;
352 Глава 5. Использование библиотечных функций /★ Основная функция программы */ void main(void) { byte command_addr; // определим переменную command_addr, II для хранения кода команды установки // адреса ячейки видеопамяти DDRAM /* инициализируем LCD 2x16 */ lcd_init(16); /* очистим LCD */ lcd_clear(); /* Вычислим значение кода команды установки адреса ячейки видеопамяти DDRAM Адреса ячеек DDRAM первой строки LCD начинаются с 0x00 Адреса ячеек DDRAM второй строки LCD начинаются с 0x40 (64) (см. LCD-функции) Чтобы получить код команды установки адреса в DDRAM, следует установить бит 7 в 1 (см. Табл. 5.35). Следующие 7 битов (0...6) отводятся для адреса ячейки видеопамяти DDRAM */ /* Выведем фразу "Hello!" в середину первой строки, т. е. первая буква должна быть выведена в 5-ю ячейку (5-е знакоместо) первой строки LCD счёт ячеек начинается с 0 */ command_addr=0bl0000101; // напомним, что 5=0Ь00000101 /* Вывод "Hello!" */ led—write_byte(command—addr,'H'); lcd_write_byte(command_addr+l,'e'); lcd_write_byte(command_addr+2,'1'); lcd_write_byte(command_addr+3,'!'); lcd_write_byte(command_addr+4, ' о') ; lcd_write_byte(command_addr+5,'!'); /* Выведем фразу "Привет" в середину второй строки, т. е. первая буква должна быть выведена в 5-ю ячейку (5-е знакоместо) второй строки LCD Счёт ячеек начинается с 0 Адрес требуемой ячейки видеопамяти DDRAM: 64+5=69 */ command_addr=0bll000101; // напомним, что 69=0Ь01000101 /* Вывод "Привет" Коды символов см. Табл. 5.32 */ lcd_write_byte(command_addr,0хА8); lcd_write_byte(command_addr+l,0x70); lcd_write_byte(command_addr+2,0xB8); lcd_write_byte(command_addr+3,0xB3); lcd_write_byte(command_addr+4,0x65);
5.15. LCD-функции 353 lcd_write_byte(command_addr+5, OxBF) ; /* Завершим программу бесконечным циклом */ while (1); } // закрывающая скобка функции main Результат выполнения этой программы представлен на Рис. 5.41. Hello! Привет Рис. 5.41. Вывод текста, содержащего русские буквы. 5.15.2. LCD-функции для дисплеев с 4x40 символов Эти LCD-функции предназначены для облегчения пользователю сопряжения между программами Си и алфавитно-цифровыми LCD-модулями с 4x40 симво- лов, со встроенным чипом HD44780 от Hitachi или эквивалентным. Несколько слов о подключении LCD-модулей формата 4x40. Эти модули со- держат два контроллера HD44780 и фактически представляют собой два модуля 2x40. Для сокращения числа соединений все сигналы обоих контроллеров соеди- нены параллельно, исключение составляет только сигнал Е, одновременно вы- полняющий роль тактового сигнала и сигнала выборки микросхемы CS, поэтому обмен с каждым из контроллеров ведётся попеременно. Поэтому у этих модулей два сигнала Е: Е1 и Е2. Перечень LCD-функций для дисплеев с 4x40 символов и их действия приве- дены в Табл. 5.38. Таблица 5.38. LCD-функции для дисплеев с 4x40 символов Функция Действие LCD-функции низкого уровня void _lcd_ready(void) Эта функция ждёт, пока модуль LCD не будет готов принять данные. Эта функция должна быть вызвана до записи данных в LCD функцией _lcd_write_data void lcd_write_byte (unsigned char addr, unsigned char data) Эта функция записывает байт data в ОЗУ знакогенератора (CGRAM) или в DDRAM LCD. Переменная addr представляет собой код команды для установки адреса ячейки памяти ОЗУ знакогенератора (CGRAM) или DDRAM LCD (см. Табл. 5.35). Её значение для адресации CGRAM: addr=0x01XXXXXX, где ХХХХХХ — адрес ячейки CGRAM, в которую следует записать байт data. Для адресации DDRAM: addr=0xlXXXXXXX, где ХХХХХХХ — адрес ячейки DDRAM, в которую следует записать байт data. Глобальная переменная _enl_msk должна быть установлена в LCD_EN1 или в LCD_EN2, чтобы выбрать контроллер верхней или соответственно нижней половиной LCD
354 Глава 5. Использование библиотечных функций (продолжение) Функция Действие void _lcd_write_data (unsigned char data) Эта функция записывает байт data в регистр инструкций LCD. Эта функция может быть использована для изменения конфигурации LCD. Сначала должны быть вызваны функции низкого уровня _lcd_ready и _lcd_write_data, глобальная переменная _enl_msk должна быть установлена в LCD_EN1 или соответственно LCD_EN2, чтобы выбрать контроллер верхней или соответственно нижней половиной LCD unsigned char lcd_read_byte (unsigned char addr) Эта функция читает байт из ОЗУ знакогенератора (CGRAM) или из DDRAM LCD. Переменная addr представляет собой код команды для установки адреса ячейки памяти ОЗУ знакогенератора (CGRAM) или DDRAM LCD (см. Табл. 5.35). Её значение для адресации CGRAM: addr=0x01XXXXXX, где ХХХХХХ — адрес ячейки CGRAM, из которой следует прочитать байт Для адресации DDRAM: addr=OxlXXXXXXX, где ХХХХХХХ — адрес ячейки DDRAM, из которой следует прочитать байт. Глобальная переменная _enl_msk должна быть установлена в LCD_EN1 или соответственно LCD_EN2, чтобы выбрать контроллер верхней или соответственно нижней половиной LCD LCD-функции высокого уровня void lcd_init(void) Эта функция инициализирует модуль LCD, очищает дисплей и устанавливает позицию для вывода символа в ряд 0 столбца 0. Эта функция должна быть вызвана первой, до использования других LCD-функций высокого уровня void lcd_clear(void) Эта функция очищает LCD и устанавливает позицию для вывода символа в ряд 0 столбца 0 void lcd_gotoxy (unsigned char x, unsigned char y) Эта функция устанавливает текущую позицию дисплея в столбец х и ряду. Нумерация рядов и столбцов начинается с 0 void lcd_putchar(char c) Эта функция отображает символ с в текущей позиции дисплея void !cd_puts(char *str) Эта функция отображает в текущей позиции дисплея строку str, расположенную в SRAM void lcd_putsf(char flash *str) Эта функция отображает в текущей позиции дисплея строку str, расположенную во FLASH Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций определены в файле lcd4x40.h, расположенном в поддиректории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл lcd4x40.h. До подключения файла lcd4x40.h необходимо объявить, какой порт микро- контроллера будет использован для связи с модулем LCD. Синтаксис: #asm .equ _1сб_рог1=адрес_порта #endasm
5.15. LCD-функции 355 Адрес соответствующего порта можно найти в файле .h, который соответству- ет используемому микроконтроллеру. Файлы с расширением .h расположены в поддиректории ..\INC. Пример: Использование LCD-функций для дисплеев с 4x40 символов #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла lcd4x40.h объявим порт, используемый для подключения модуля LCD */ /* Используем Порт В микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; */ #asm .equ _lcd_port=0xl8 #endasm /* Теперь перед использованием LCD-функций для дисплеев с 4x40 символов подключим заголовочный файл lcd4x40.h */ tfinclude <lcd4x40.h> Описание используемого LCD (Рис. 5.42) можно найти на сайте производите- ля (например, на сайтах фирм МЭЛТ http://www.melt.aha.ru/ или Data Vision http://www.datavision.com.tw/). а) LCD1 LCD 4x40 symbols___LCD 4x40 symbols____ LCD 4x40 symbols___LCD 4x40 symbols____ LCD 4x40 symbols___LCD 4x40 symbols____ LCD 4x40 symbols___LCD 4x40 symbols____ HS SS 85888888 6) Puc. 5.42. LCD 4x40 символов — внешний вид (a) и назначение выводов (б).
356 Глава 5. Использование библиотечных функций LCD-модуль должен быть подключён к битам порта в соответствии с Табл. 5.39. Таблица 5.39. Подключение LCD-модуля 4x40 символов к порту микроконтроллера Вывод LCD (сигнал) Бит порта Вывод И (сигнал RS) Бит 0 Вывод 10 (сигнал R/W) Бит 1 Вывод 9 (сигнал Е1) Бит 2 Вывод 15 (сигнал Е2) БитЗ Вывод 4 (сигнал DB4) Бит 4 Вывод 3 (сигнал DB5) Бит 5 Вывод 2 (сигнал DB6) Бит 6 Вывод 1 (сигнал DB7) Бит 7 Разумеется, согласно описанию на LCD, необходимо подключить питание LCD и напряжение для управления контрастностью. Пример подключения LCD с 4x40 символов к микроконтроллеру AT90S2313 показан на Рис. 5.43. Цепь сброса, пита- ния и кварцевый резонатор микроконтроллера не показаны (см. Рис. 4.1). DD1 AT90S2313 VCC < LCD1 Рис. 5.43. Пример подключения LCD 4x40 к Порту В микроконтроллера AT90S2313. Пример: /* Использование LCD-функций низкого уровня для дисплеев с 4x40 символов */ /* Используется LCD 4x40, микроконтроллер AT90S2313. Схема подключения см. Рис. 5.43. */ /* Выведем изображение курсора в виде мигающего знакоместа в начало 3-й строки дисплея (счёт строк начинается с 1) */
5.75. LCD-функции 357 #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Перед подключением файла lcd4x40.h объявим порт, используемый для подключения модуля LCD */ /* Используем Порт В микроконтроллера AT90S2313 Адрес порта находим в файле 90s2313.h в строке: sfrb PORTB=Oxl8; */ #asm .equ ____lcd_port=0xl8 ftendasm /* Теперь перед использованием LCD-функций для дисплеев с 4x40 символов подключим заголовочный файл lcd4x40.h */ ^include <lcd4x40.h> /* Основная функция программы */ void main(void) { _enl_msk=LCD_EN2; и выберем контроллер нижней половины LCD _lcd_ready(); // ждём, пока модуль LCD не будет готов И принять данные _lcd_write_data(0x01); II очищаем экран _lcd_write_data(0x02); и установим курсор в нулевую позицию и 3-й строки дисплея (если счёт строк и начинать с 1) _lcd_write_data(0x0f); II Включим экран, установим курсор, изображение и курсора сделаем в виде мигающего знакоместа и (см. Табл. 5.33...5.32) while (1); II закончим программу бесконечным циклом } См. также примеры в предыдущем пункте. Только следует учитывать, что LCD 4x40 имеет два контроллера, и обращение к каждому из них должно быть выпол- нено отдельно, как указано выше. 5.15.3. LCD-функции для дисплеев, подключённых в режиме отображения 8-битовой памяти Эти функции LCD предназначены для облегчения пользователю сопряжения между программами Си и алфавитно-цифровыми LCD-модулями со встроенным чипом от Hitachi HD44780 или эквивалентным. LCD должен быть подключён к внешним шинам данных и адреса микроконтроллера AVR как 8-битное перифе- рийное устройство. Этот тип связи использован на платах разработчика STK200+ и STK300 от Kanda Systems. При использовании данных функций и при указанном подключении LCD в настройках проекта на закладке С Compiler (Компилятор Си) в опции Memory
358 Глава 5. Использование библиотечных функций Model (Модель памяти) (см. Закладка С Compiler (Компилятор Си)) следует вы- брать модель памяти Small (Рис. 5.44). Модель памяти Tiny позволяет иметь до- ступ только к первым 256 байтам SRAM микроконтроллера. Рис. 5.44. Выбор модели Small в опции Memory Model (Модель памяти). Обратите внимание, что эти функции могут использоваться только с теми чипами AVR, которые допускают использование внешних устройств памяти: AT90S4414, AT90S8515, ATmega603, ATmegalO3, ATmegal28, ATmegal61 и ATmegal62. Перечень LCD-функций для дисплеев, подключённых в режиме отображения 8-битовой памяти, и их действия приведены в Табл. 5.40. Таблица 5.40. LCD-функции для дисплеев, подключённых в режиме отображения 8-битовой памяти Функция Действие LCD-функции низкого уровня void _lcd_ready(void) Эта функция ждёт, пока модуль LCD не будет готов принять данные. Эта функция должна быть вызвана до записи данных в LCD макросами _LCD_RS0 и _LCD_RS1. Макросы _LCD_RS0 и _LCD_RS1 используются для доступа к LCD Instruction Register (Регистру инструкции LCD) с RS = 0 и соответственно RS = 1.
5.15. LCD-функции 359 (продолжение) Функция Действие void lcd_write_byte (unsigned char addr, unsigned char data) Эта функция записывает байт data в ОЗУ знакогенератора (CGRAM) или в DDRAM LCD. Переменная addr представляет собой код команды для установки адреса ячейки памяти ОЗУ знакогенератора (CGRAM) или DDRAM LCD (см. Табл. 5.35). Её значение для адресации CGRAM: addr=0x01 ХХХХХХ, где ХХХХХХ — адрес ячейки CGRAM, в которую следует записать байт data. Для адресации DDRAM: addr=OxlXX)OOCXX, где ХХХХХХХ — адрес ячейки DDRAM, в которую следует записать байт data unsigned char lcd_read_byte (unsigned char addr) Эта функция читает байт из ОЗУ знакогенератора (CGRAM) или из DDRAM LCD. Переменная addr представляет собой код команды для установки адреса ячейки памяти ОЗУ знакогенератора (CGRAM) или DDRAM LCD (см. Табл. 5.35). Её значение для адресации CGRAM: addr=0x01 ХХХХХХ, где ХХХХХХ — адрес ячейки CGRAM, из которой следует прочитать байт. Для адресации DDRAM: addr=OxlXXX?OCXX, где ХХХХХХХ — адрес ячейки DDRAM, из которой следует прочитать байт. LCD-функции высокого уровня void Icdjnit (unsigned char lcd_columns) Эта функция инициализирует модуль LCD, очищает дисплей и устанавливает позицию для вывода символа в ряд 0 столбца 0. Должно быть определено количество столбцов LCD (lcd_cohmms), например, 16. Курсор не отображается. Эта функция должна быть вызвана первой, до использования других LCD void lcd_clear(void) Эта функция очищает LCD и устанавливает позицию для вывода символа в ряд 0 столбца 0 void lcd_gotoxy (unsigned charx, unsigned char y) Эта функция устанавливает текущую позицию дисплея в столбец х и ряду. Нумерация рядов и столбцов начинается с 0 void lcd_putchar(char c) Эта функция отображает символ с в текущей позиции дисплея void Icd_puts(char *str) Эта функция отображает в текущей позиции дисплея строку str, расположенную в SRAM void lcd_putsf(char flash *str) Эта функция отображает в текущей позиции дисплея строку str, расположенную во FLASH Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций определены в файле Icdstk.h, расположенном в под- директории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл Icdstk.h, В Icdstk.h поддерживаются следующие форматы LCD: 1x8, 2x12, 3x12, 1x16, 2x16, 2x20, 4x20, 2x24 и 2x40 символов. Пример: /* Использование LCD-функций для дисплеев, подключённых в режиме отображения 8-битовой памяти */ #include <90s8515.h> // подключаем заголовочный файл используемого
360 Глава 5. Использование библиотечных функций // микроконтроллера AVR /* Теперь перед использованием LCD-функций для дисплеев, подключённых в режиме отображения 8-битовой памяти, подключим заголовочный файл Icdstk.h */ tfinclude <lcdstk.h> Схема подключения LCD к микроконтроллеру AT90S8515 на плате разработ- чика STK200 показана на Рис. 5.45. Цепь питания, сброса и кварцевый резонатор микроконтроллера не показаны (см. Рис. 4.2). DD1 AT90S8515 XTAL1 XTAL2 РВО/ТО РВ1/Т1 PB2/A1N0 PB3/A1N1 PB4/SS PB5/MOSJ PB6/MISO PB7/SCK PD0/RXD PD1/TXD PD2/1NT0 PD3/1NT1 PD4 РО5/ОС1А PO6/WR PD7/TO РАО/АОО PA1/AD1 PA2/AD2 PA3/AD3 PA4/AD4 PA5/AD5 PA6/AD6 PA7/AD7 РСО/Ав РС1/А9 РС2/А10 РСЗ/А11 РС4/А12 РС5/А13 РС6/А14 РС7/А15 39 38 37 36 35 34 33 32 21 22 23 24 25 26 27 А14 10 28___________ ALE ОС1В ICP LCD1 WR 1 5 9 Рис. 5.45. Схема подключения LCD к микроконтроллеру AT90S8515 на плате STK200. Пример: /* Использование LCD-функций для дисплеев, подключённых в режиме отображения 8-битовой памяти */ /* Используется LCD 2x16, микроконтроллер AT90S8515. Схема подключения см. Рис. 5.45. */ /* Определим символ пользователя и выведем его изображение на LCD */ #include <90s8515.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Теперь перед использованием LCD-функций для дисплеев,
5.15. LCD-функции 361 подключённых в режиме отображения 8-битовой памяти, подключим заголовочный файл Icdstk.h */ #include <lcdstk.h> /* Определим тип данных byte byte==unsigned char */ typedef unsigned char byte; /* Определим номер символа. Для примера определим символ с номером 4, т. е. он будет располагаться в ОЗУ знакогенератора (CGRAM) и его код будет 0x04 (см. Табл. 5.32) . Всего можно определить 8 символов 0...7 с кодами 0х00...0х07 */ byte number_char=4; /* Таблицу (массив) для определения символа пользователя ( см. Рис. 5.34) для примера разместим в SRAM микроконтроллера (хотя можно и во FLASH-памяти) */ byte char4[8]= {ObOOOOlllO, ObOOOlOOOl, ObOOOllOll, ObOOOlOlOl, ObOOOlOOOl, ObOOOlOlOl, ObOOOOlllO, ObOOOOOOOO); /* Основная функция программы */ void main(void) { byte i; // определим переменную i, используемую // как индекс byte command_addr; // определим переменную command_addr, // для хранения кода команды установки // адреса ячейки памяти ОЗУ знакогенератора // (CGRAM) (см. Табл. 5.35) /* инициализируем LCD 2x16 */ lcd_init(16); /* Вычислим значение кода команды установки адреса самого младшего байта символа 4 в CGRAM Так как под каждый символ отводится 8 Б, то байты 0-го символа имеют адреса 0х00...0х07, байты 1-го - 0x08...0x0F, ... байты п-го - (п*8) - (п*8+7) Так как чтобы получить адрес самого младшего байта символа, надо номер символа (нумерация начинается с 0) умножить на 8 Чтобы получить код команды установки адреса в CGRAM, следует установить бит б в 1, а7-в0. Это можно
362 Глава 5. Использование библиотечных функций сделать, прибавив к адресу самого младшего байта символа число ОЬОЮООООО, т. к. для указания адреса ячеек CGRAM отводится б младших битов */ command_addr= (number_char*8) + 0601000000; /* Перепишем значения таблицы для определения символа пользователя (char4[8]) в ячейки символа 4 CGRAM */ for (i=0; i<8; i++) // цикл for (см. Оператор for) { /* Тело цикла for */ lcd_write_byte(command_addr++,char4[i]); } /* Установим курсор в начальную позицию LCD */ lcd_gotoxy(0,0); /* Выведем строку */ lcd_putsf("Symbol number 4 of User: "); /* Отобразим определённый ранее символ 4 пользователя */ lcd_putchar(4); // 4 - это код символа в CGRAM // Можно записать 0x04 (см. Табл. 5.35) /* Обратите внимание, что этот символ дублируется ещё и кодом ОхОС, т. е. символ также можно вывести командой: lcd„putchar(ОхОС); */ /* Завершим программу бесконечным циклом */ while (1); } // закрывающая скобка функции main Результат выполнения этой программы представлен на Рис. 5.46. Symbol number 4 of Users 9______ 85333883 Рис. 5.46. Вывод символа 4, определённого пользователем. Обратите внимание, что если в строке, которая выводится на LCD, сим- волов больше, чем в строке LCD, то вывод продолжается на следующей строке.
5.15. LCD-функции 363 Пример: /* Использование LCD-функций для дисплеев, подключённых в режиме отображения 8-битовой памяти ★/ /★ Используется LCD 2x16 с русифицированным знакогенератором, микроконтроллер AT90S8515. Схема подключения см. Рис. 5.45. */ /* Эту программу можно использовать для просмотра прошивки знакогенератора LCD */ /* В верхней строке LCD с задержкой в 1 сек будут выводиться коды символов от 0 до 255, а в нижней строке - изображение символа с этим кодом всё это циклически повторяется */ tfinclude <90s8515.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Теперь перед использованием LCD-функций для дисплеев, подключённых в режиме отображения 8-битовой памяти, подключим заголовочный файл Icdstk.h */ tfinclude <lcdstk.h> /* подключим функции ввода/вывода */ tfinclude <stdio.h> /* подключим функции задержки */ tfinclude <delay.h> char string_LCD[20]; // определим строку для вывода на LCD, т. е. // зарезервируем место в SRAM под массив /! из 20 символов /* Основная функция программы */ void main(void) { unsigned int code_symbol; /* Инициализируем LCD 2x16 */ lcd_init(16); while (1) // бесконечный цикл while { /* Тело цикла while (см. Оператор while) */ /* Оператор цикла for (см. Оператор for) */ for (сode_symbo1=0x00; code_symbol<=255; code_symbol++) { /* Очистим LCD и установим курсор в ряд 0 столбца 0 */ lcd_clear();
364 Глава 5. Использование библиотечных функций /★ Сформируем строку string_LCD для вывода на LCD (см. Стандартные функции ввода/вывода) */ tfpragma ruslcd+ // Включим автоматическую перекодировку // символов кириллицы sprintf(string_LCD,"Код: 0х%02х", code_symbol); ttpragma ruslcd- // Выключим автоматическую перекодировку // символов кириллицы /* Функция sprintf осуществляет форматные преобразования и помещает результат в строку string_LCD Спецификация преобразования "Код: 0х%02х\п" означает, что сначала будет выведена последовательность "Код: Ох", затем значение переменной code_syinbol в шестнадцатеричном виде символами нижнего регистра (символ преобразования 'х'), причём будет выведено, по крайней мере, 2 символа. Если результат имеет менее чем 2 символа, то слева он заполнится нулями (описатель width '02') */ /* Выведем строку string_LCD на LCD в позицию курсора */ lcd_puts(string_LCD); /* Установим курсор в начало второй строки LCD */ lcd_gotoxy(0,1); /* Аналогичным способом можно было вывести и вторую строку: sprintf(string_LCD, "Символ: %с", code_symbol); lcd_puts(string_LCD); но чтобы показать использование других LCD-функций для дисплеев до 2x40 символов, сделаем вывод второй строки другим способом */ /* Вывод второй строки на LCD в позицию курсора */ tfpragma ruslcd+ // Включим автоматическую перекодировку // символов кириллицы lcd_putsf("Символ:"); ttpragma ruslcd- // Выключим автоматическую перекодировку // символов кириллицы /★ Выведем в текущую позицию курсора строку "Символ" Обратите внимание, что здесь нужно использовать функцию lcd_putsf, а не lcd_puts, т. к. при такой записи строка "Symbol" располагается во FLASH-памяти, а не в SRAM */ /* Установим курсор в позицию 10 второй строки LCD (счёт позиций начинается с 0) */ lcd_gotoxy(10,1); /* Выведем в текущую позицию курсора символ, код которого
5.15. LCD-функции 365 соответствует значению code_symbol */ lcd_putchar(code_symbol); /* Сделаем задержку delay_ms(1000); }; code_symbol=0x00; }; в 1 сек */ // закрывающая скобка для цикла for // обнулим значение code_symbol и всё повторим // закрывающая скобка для цикла while // закрывающая скобка для функции main Результат выполнения этой программы представлен на Рис. 5.47. Код: 0x21 Символ? !_________ $ Q Ш со 5 >>> OtOCUJ QQQQQQQQ Рис. 5.47. Вывод кода и изображения символа знакогенератора. В файле Icdstk.h, кроме указанных функций, определены два макроса — LCD-RS0 и LCD RS1. Эти макросы также можно использовать для осущест- вления доступа к LCD-дисплеям, подключённым в режиме отображения 8-бито- вой памяти. Перед КАЖДЫМ использованием макросов _LCD_RS0 и _LCD_RS1 долж- на быть вызвана функция _lcd_ready(). Макрос _LCD_RS0 записывает байт (код команды) в регистр инструкций LCD. Этот макрос может быть использован для изменения конфигурации LCD. Синтаксис: _LCD_RS0 = код_команды; Макрос LCD RS0 по своему действию аналогичен функции _lcd_write_data из двух предыдущих перечней LCD-функций (см. LCD-функции для дисплеев до 2x40 символов, и LCD-функции для дисплеев с 4x40 символов). Пример: /* Использование макроса _LCD_RS0 для дисплеев, подключённых в режиме отображения 8-битовой памяти */ /* Используется LCD 2x16, микроконтроллер AT90S8515. Схема подключения см. Рис. 5.45. */ /* Выведем изображение курсора в виде мигающего знакоместа в нулевую позицию дисплея */ Hnclude <90s8515.h> // подключаем заголовочный файл используемого
366 Глава 5. Использование библиотечных функций II микроконтроллера AVR /* Теперь перед использованием макроса _LCD_RS0 подключим заголовочный файл Icdstk.h */ tfinclude <lcdstk.h> /* Основная функция программы */ void main(void) { /* инициализация для LCD 2x16 */ lcd_init(16); _lcd_ready(); // ждём, пока модуль LCD не будет готов // принять данные _LCD_RS0=0x0f; // Включим экран, включим курсор, изображение // курсора сделаем в виде мигающего знакоместа // (см. Табл. 5.33...5.35) while (1) ; // закончим программу бесконечным циклом } Макрос _LCD_RS1 по своему действию аналогичен функции lcd_putchar (см. Табл. 5.40). Синтаксис: _LCD_RS1= код_символа; ИЛИ _LCD_RS1= 'символ'; Применение макросов LCDRS0 и _LCD_RS1 позволяет уменьшить раз- мер сгенерированного кода программы. Пример: /* Использование LCD-функций и макросов для дисплеев, подключённых в режиме отображения 8-битовой памяти */ /* Используется LCD 2x16, микроконтроллер AT90S8515. Схема подключения см. Рис. 5.45. */ /* Демонстрируется вывод текста, содержащего русские буквы */ tfinclude <90s8515.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Теперь перед использованием LCD-функций и макросов для дисплеев, подключённых в режиме отображения 8-битовой памяти, подключим заголовочный файл Icdstk.h */ tfinclude <lcdstk.h>
5.15. LCD-функции 367 /* Определим тип данных byte byte==unsigned char ★/ typedef unsigned char byte; /* Основная функция программы */ void main(void) { /* инициализируем LCD 2x16 */ lcd_init(16); _lcd_ready(); // ждём, пока модуль LCD не будет готов // принять данные /* очистим LCD; установим позицию для вывода символа в ряд 0 столбца 0 */ lcd_clear(); /* Выведем слово "Makros" в середину верхней строки LCD, т. е. первая буква должна быть выведена в 5-ю ячейку (5-е знакоместо) верхней строки LCD счёт ячеек начинается с 0 */ /* Сначала переместим курсор на 5 позиций вправо Для этого выберем для макроса _LCD_RS0 код команды сдвига курсора вправо на одну позицию (см. Табл. 5.35) и повторим этот макрос 5 раз Так как в коде команды два самых младших бита могут быть любыми, то можно использовать код от 0x14 до 0x17 */ _lcd_ready(); // ждём, пока модуль LCD не будет готов LCD_RS0=0xl4; // принять данные // Сдвинем курсор на одну позицию вправо /* Повторим еще 4 раза */ _lcd_ready(); _LCD_RS0 = 0xl4; _lcd_ready(); _LCD_RS0 = 0xl5; _lcd_ready(); _LCD_RS0=0xl6; _lcd_ready(); _LCD_RS0 = 0xl7 ; /* Теперь в текущую позицию выведем слово "Makros" */ _lcd_ready(); _LCD_RS1='М'; _lcd_ready(); _LCD_RSl='a'; _lcd_ready(); _LCD_RS1='k';
368 Глава 5. Использование библиотечных функций _lcd_ready(); _LCD.RSl='r'; _lcd_ready(); _LCD.RS1='о'; _lcd_ready(); _LCD.RSl='s'; /* Выведем слово "Макрос" в середину второй строки, т. е. первая буква должна быть выведена в 5-ю ячейку (5-е знакоместо) второй строки LCD */ /* Сначала переместим курсор в 5-ю позицию нижней строки LCD */ lcd_gotoxy(5,1); /* Теперь в текущую позицию выведем слово "Макрос" Коды символов см. Табл. 5.32 */ _lcd_ready(); _LCD.RSl=0x4d; _lcd_ready(); _LCD_RSl = 0x61; _lcd_ready(); _LCD.RSl=0xba; _lcd_ready(); _LCD_RS1=0x70; _lcd_ready(); _LCD.RSl=0x6f; _lcd_ready(); _LCD.RSl=0x63; /* Завершим программу бесконечным циклом */ while (1); } // закрывающая скобка функции main Результат выполнения этой программы представлен на Рис. 5.48. Makгos Макрос Рис. 5.48. Вывод текста, содержащего русские буквы, с использованием макросов. 5.16. Функции управления питанием Функции управления питанием предназначены для перевода чипа AVR в один из его режимов пониженного энергопотребления.
5.16. Функции управления питанием 369 Перечень функций управления питанием и их действия приведены в Табл. 5.41. Таблица 5.41. Перечень функций управления питанием Функция Действие void sleep_enable(void) Эта функция разрешает перевод в режим пониженного энергопотребления void sleep_disable(void) Эта функция запрещает случайный перевод в режим пониженного энергопотреблен ия void idle(void) Эта функция переводит чип AVR в режим холостого хода (Idle mode). До использования этой функции должна быть вызвана функция sleep_enable, чтобы разрешить перевод в режим пониженного энергопотребления. В этом режиме CPU останавливается, нотаймеры/счётчики, сторожевой таймер (Watchdog) и система прерываний продолжают работать. CPU можно разбудить внешними и внутренними прерываниями void powerdown(void) Эта функция переводит чип AVR в экономичный режим (Power Down Mode). До использования этой функции должна быть вызвана функция sleep_enable, чтобы разрешить перевод в режим пониженного энергопотребления. В этом режиме останавливается внешний генератор тактовых импульсов. Чип AVR можно разбудить только внешним сбросом, окончанием периода сторожевого таймера (Witchdog) или внешним прерыванием по уровню void powersave(void) Эта функция переводит чип AVR в режим сохранения энергии (Power Save Mode). До использования этой функции должна быть вызвана функция sleep_enable, чтобы разрешить перевод в режим пониженного энергопотребления. Этот режим подобен экономичному режиму (Power Down Mode) с некоторыми различиями. Обратитесь к описанию (datasheet) от Atmel для конкретного микроконтроллера, который используется (см. Команда Help -> AVR Data Sheets (Помощь -> Описания AVR)) void standby(void) Эта функция переводит чип AVR в режим ожидания (Standby mode). До использования этой функции должна быть вызвана функция sleep_enable, чтобы разрешить перевод в режим пониженного энергопотребления. Этот режим подобен экономичному режиму (Power Down Mode), за исключением того, что внешний генератор тактовых импульсов продолжает работать. Обратитесь к описанию (datasheet) от Atmel для конкретного микроконтроллера, чтобы проверить, доступен ли для него режим ожидания (Standby mode) (см. Команда Help AVR Data Sheets (Помощь -> Описания AVR)) void extended_standby(void) Эта функция переводит чип AVR в расширенный режим ожидания (Extended Standby mode). До использования этой функции должна быть вызвана функция sleep_enable, чтобы разрешить перевод в режим пониженного энергопотребления. Этот режим подобен режиму сохранения энергии (Power Save Mode), за исключением того, что внешний генератор тактовых импульсов продолжает работать. Обратитесь к описанию (datasheet) от Atmel для конкретного микроконтроллера, чтобы проверить, доступен ли для него расширенный режим ожидания (Extended Standby mode) (см. Команда Help -> AVR Data Sheets (Помощь -> Описания AVR))
370 Глава 5. Использование библиотечных функций Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций размещаются в файле sleep.h, расположенном в поддиректории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл sleep.h. Пример: /* Использование функций управления питанием */ /* Микроконтроллер AT90S2313 */ ttinclude <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Подключим функции управления питанием ★/ tfinclude <sleep.h> /* Основная функция программы */ void main(void) { void sleep_enable; // разрешим перевод чипа в режим // пониженного энергопотребления void idle; // переведем чип в режим холостого хода (Idle mode) 5.17. Функции задержки Эти функции предназначены для генерации задержек в программах Си. Перечень функций задержки и их действия приведены в Табл. 5.42. Таблица 5.42. Перечень функций задержки Функция Действие void delay_us(unsigned int n) Эта функция генерирует задержку п микросекунд, п должно быть постоянным выражением void delay_ms(unsigned int n) Эта функция генерирует задержку п миллисекунд. Эта функция автоматически восстанавливает сторожевой таймер (Witchdog) каждую 1 мс, генерируя инструкцию wdr. п должно быть постоянным выражением Обратите внимание, что все функции должны быть записаны в одну стро- ку. Переносы НЕ ДОПУСКАЮТСЯ! Прототипы этих функций размещаются в файле delay.h, расположенном в поддиректории ..\INC. Перед использованием этих функций директивой #include должен быть подключён файл delay.h.
5.7 7. Функции задержки 371 До вызова этих функций должны быть запрещены прерывания, в противном случае задержки будут значительно дольше, чем ожидается. Также очень важно определить правильную тактовую частоту чипа AVR при конфигурировании про- екта (см. Закладка С Compiler (Компилятор Си)). Пример: /* Использование функций задержки */ /* Микроконтроллер AT90S2313 */ #include <90s2313.h> // подключаем заголовочный файл используемого // микроконтроллера AVR /* Подключим функции задержки */ tfinclude <delay.h> /* Основная функция программы */ void main(void) { #asm("cli") delay_us(150); // запретим прерывания // сгенерируем задержку 150 мкс delay_ms(15); // сгенерируем задержку 15 мс #asm("sei") // разрешим прерывания
ГЛАВА АВТОМАТИЧЕСКИЙ ГЕНЕРАТОР ПРОГРАММ CodeWizardAVR В CodeVisionAVR имеется средство для облегчения разработчику написания программы. Это так называемый мастер — Автоматический генератор программ CodeWizardAVR. CodeWizardAVR позволяет по заданию разработчика автоматически сгенери- ровать весь необходимый код для выполнения таких функций, как: • организация доступа к внешней памяти; • сброс и исходная идентификация чипа; • инициализация портов ввода/вывода; • инициализация внешних прерываний; • инициализация таймеров/счётчиков; • инициализация сторожевого (Watchdog) таймера; • инициализация UART (USART) и прерывания, управляющего буфером последовательной связи; • инициализация аналогового компаратора; • инициализация АЦП; • инициализация интерфейса SPI; • инициализация шины 12С, температурного датчика LM75, термометра/тер- мостата DS1621, часов реального времени PCF8563, PCF8583, DS1302 и DS1307; • инициализация шины 1-Wire и температурных датчиков DS1820/DS18S20; • инициализация LCD-модуля. При создании нового проекта разработчику будет сразу предложено исполь- зовать Автоматический программный генератор CodeWizardAVR (см. Команда File New (Файл Новый)). Кроме того, CodeWizardAVR можно запустить на лю- бом этапе работы над проектом (см. Команда Tools CodeWizardAVR (Инструмен- ты CodeWizardAVR)). При запуске Автоматического программного генератора появится диалоговое окно CodeWizardAVR (Рис. 6.1).
6.1. Строка меню 373 Рис. 6.1. Диалоговое окно CodeWizardAVR. Как и основное окно программы, это диалоговое окно имеет строку меню (1), а также набор различных закладок (2). 6.1. Строка меню Строка меню позволяет выбрать команды из различных меню. В этой строке выведены названия меню, которые предоставляют доступ к их командам. 6.1.1. Меню File (Файл) В состав меню Pile (Файл) входит несколько команд. В Табл. 6.1 даётся крат- кое описание этих команд. Таблица 6.1. Команды меню File (Файл) Команда Назначение New (Новый) Позволяет создать новый проект CodeWizardAVR Open (Открыть) Позволяет загрузить существующий проект CodeWizardAVR Save (Сохранить) Позволяет сохранить открытый к настоящему времени проект CodeWizardAVR Save As... (Сохранить Как...) Позволяет сохранить открытый к настоящему времени проект CodeWizardAVR под новым именем
374 Глава 6. Автоматический генератор программ Code WizardA VR (продолжение) Команда Назначение Program Preview (Предпросмотр программы) Позволяет просмотреть код, сгенерированный CodeWizardAVR Generate, Save and Exit (Генерировать, сохранить и выйти) Позволяет сгенерировать код, сохранить файл проекта CodeWizardAVR и вернуться в IDE CodeVisionAVR Exit( Выход) Позволяет выйти из CodeWizardAVR, не генерируя никаких программных файлов Команда File -> New (Файл -> Новый) Эта команда позволяет создать новый проект CodeWizardAVR. Этот проект по умолчанию будет назван untitled.cwp. Команда File -> Open (Файл -> Открыть) Эта команда позволяет загрузить существующий проект CodeWizardAVR. После выбора этой команды появится диалоговое окно Open Project (Открыть проект) (Рис. 6.2).В этом окне следует выбрать требуемый файл проекта и щёлк- нуть по кнопке Открыть. Рис. 6.2. Выбор требуемого файла проекта CodeWizardAVR. Выбранный файл будет загружен в CodeWizardAVR. Команда File Save (Файл -> Сохранить) Эта команда сохраняет открытый CodeWizardAVR под тем же именем. к настоящему времени проект Команда File -> Save As... (Файл -> Сохранить как...) Эта команда позволяет сохранить открытый к настоящему времени проект CodeWizardAVR под новым именем. После выбора этой команды появится диалоговое окно Save...As (Сохра- нить...Как), показанное на Рис. 6.3.
6.1. Строка меню 375 Рис. 6.3. Диалоговое окно Save...As (Сохранить...Как). В этом окне следует ввести новое имя файла и щёлкнуть по кнопке Сохранить. При этом файл будет сохранён под новым именем. Обратите внимание, что файл со старым именем не будет удалён и оста- нется на диске. Таким образом, эта команда позволяет сохранять различные версии одного и того же файла проекта CodeWizardAVR. Команда File Program Preview (Файл Предпросмотр программы) Эта команда позволяет в окне редактора просмотреть код, сгенерированный CodeWizardAVR. После выбора этой команды в окне редактора появится окно CodeWizardAVR Program Preview (CodeWizardAVR Предпросмотр программы) со сгенерирован- ным кодом (Рис. 6.4). Рис. 6.4. Окно CodeWizardAVR Program Preview (CodeWizardAVR Предпросмотр программы) со сгенерированным кодом.
376 Глава 6. Автоматический генератор программ Code WizardA VR Этим можно воспользоваться при внесении изменений в существующий про- ект: части кода, сгенерированного CodeWizardAVR, можно скопировать в буфер обмена и затем вставить в исходные файлы проекта. Пример. Предположим, что в какой-то части разрабатываемой программы требуется проинициализировать Порт В микроконтроллера AT90S2313 с кварцем 4 МГц (в данном случае частота кварца не принципиальна) так, чтобы выводы (биты) пор- та были сконфигурированы в соответствии с Табл. 6.2. Таблица 6.2. Конфигурация выводов Порта В Вывод Порта В Направление данных Выходное значение Подтягивающий резистор Bit 0 Выход (OUT) 0 — Bit 1 Выход (OUT) 0 — Bit 2 Выход (OUT) 1 — Bit 3 Выход (OUT) 1 — Bit 4 Вход (IN) — Есть Bit 5 Вход (IN) — Есть Bit 6 Вход (IN) — Нет (третье состояние) Bit 7 Вход (IN) — Нет (третье состояние) Запускаем CodeWizardAVR (см. Команда Tools CodeWizardAVR (Инструмен- ты -> CodeWizardAVR)). Сначала на закладке Chip (Чип) (см. Закладка Chip (Чип)) выбираем соответс- твующий микроконтроллер и тактовую частоту (Рис. 6.5). Рис. 6.5. Закладка Chip (Чип) с заданными опциями.
6.1. Строка меню 377 Затем переходим на закладку Ports (Порты) (см. Закладка Parts (Порты)), вы- бираем закладку Port В (Порт В) и в соответствии с Табл. 6.2 задаём конфигура- цию выводов Порта В (Рис. 6.6). Рис. 6.6. Закладка Port В (Порт В) с заданными опциями на закладке Ports (Порты). Остальные периферийные устройства оставим установленными по умолча- нию. После этого выбираем команду File Program Preview (Файл -> Предпро- смотр программы), в открывшемся окне CodeWizardAVR Program Preview (CodeWizardAVR Предпросмотр программы) находим часть кода, которая начи- нается комментарием Port В initialization (Инициализация Порта В), выделяем её и копируем в буфер обмена (Рис. 6.7). Рис. 6.7. Блок инициализации Порта В в окне CodeWizardAVR Program Preview (CodeWizardAVR Предпросмотр программы).
378 Глава 6. Автоматический генератор программ Code WizardA VR После этого данный блок можно вставить в разрабатываемую программу. В комментариях к этому блоку CodeWizardAVR описывает Функцию (Func х) и Состояние (State х) каждого вывода Порта В (например, Func3 = Out означает, что Вывод 3 будет Выходом, a State3 = 1 означает, что на Выводе 3 будет 1). Таким образом, после выполнения этого блока Порт В будет проинициализи- рован (т. е. соответствующие значения будут записаны в регистры PORTB и DDRB), согласно Табл. 6.2. Команда File -> Generate, Save and Exit (Файл -> Генерировать, сохранить и выйти) При выборе этой команды CodeWizardAVR сгенерирует основной исходник с расширением .с и файл проекта с расширением .prj, сохранит файл проекта CodeWizardAVR с расширением .cwp и вернётся в IDE CodeVisionAVR. Пользова- телю будут указаны возможные конфликты функционирования выводов микро- контроллера, и ошибки можно будет откорректировать. Пользователю будет последовательно предложено сохранить файлы с расши- рением .с, .prj или .cwp. Во всех этих окнах следует ввести имя соответствующего файла и щёлкнуть по кнопке Сохранить. После этого созданный проект будет открыт в IDE CodeVisionAVR. Команда File -> Exit (Файл -> Выход) Эта команда позволяет выйти из CodeWizardAVR, не генерируя никаких про- граммных файлов. 6.1.2. Меню Help (Помощь) В этом меню всего одна команда. Выбрав меню Help (Помощь), можно уви- деть тему помощи, которая соответствует текущему меню конфигурации CodeWizardAVR (Рис. 6.8). Рис. 6.8. Окно CodeVisionAVR С Compiler Help (Помощь по Компилятору Си CodeVisionAVR) с соответствующей темой помощи.
6.2. Закладки 379 6.2. Закладки Закладки в диалоговое окно CodeWizardAVR позволяют выбрать и задать соот- ветствующие опции того или иного периферийного устройства. В соответствии с этими настройками CodeWizardAVR сгенерирует соответствующий код. 6.2.1. Закладка Chip (Чип) Эта закладка (Рис. 6.9) позволяет выбрать чип AVR и задать его опции. Рис. 6.9. Закладка Chip (Чип). Здесь можно задать следующие опции: • Chip (Чип) — в этом окошке в выпадающем списке можно выбрать микро- контроллер AVR. Процедура выбора чипа аналогична описанной ранее (см. Закладка С Compiler (Компилятор Си)). • Clock (Тактовая частота) — в этом окошке можно задать значение тактовой частоты в мегагерцах. Это значение CodeVisionAVR будет использовать для временных вычислений, которые нужны для функций задержки, функций протокола 1-Wire и функций температурных датчиков DS1820/DS18S20 от Dallas Semiconductor (см. Использование библиотечных функций). • Значение тактовой частоты можно ввести непосредственно в окошке Clock (Тактовая частота) либо воспользоваться стрелками, расположенными справа от этого окошка. • Crystal Oscillator Divider Enabled (Разрешить делитель кварцевого генерато- ра) — вспомогательное окошко этой опции становится видимым для чипов
380 Глава 6. Автоматический генератор программ Code WizardA VR •0 CodeWizardAVR - untitled.cwp [х| I File Help External IRQ | Timers | USARTO j USART1 Analog Comparator | ADC | SPI | I2C 1 Wire | 2Wire(l2C) | LCD Bit-Banged ] Project Information Chip | External SRAM | Ports Chip: |ATmega128 AVR, которые содержат делитель кварцевого генератора. Это окошко поз- воляет разрешить или запретить делитель кварцевого генератора. •Crystal Oscillator Divider (Де- литель кварцевого генерато- ра) — вспомогательное окош- ко этой опции становится видимым, если разрешён де- литель кварцевого генерато- ра. Эта опция позволяет опре- делить коэффициент деления кварцевого генератора. •Коэффициент деления кварцевого генератора мож- но ввести непосредственно в окошке Crystal Oscillator Divider (Делитель кварцево- го генератора) либо восполь- зоваться стрелками, распо- ложенными справа от этого окошка. •Check Reset Source (Прове- рить источник сброса) — вспомогательное окошко этой опции становится види- мым для чипов AVR, которые Рис. 6.10. Вспомогательное окошко опции Program Д°пУскают идентификацию туре (Тип программы) с выпадающим списком. источника сброса. Если раз- решить эту опцию, то CodeWizardAVR сгенерирует код, который позволит идентифицировать ус- ловия, которые вызывали сброс чипа. • Program Type (Тип программы) — вспомогательное окошко этой опции с выпадающим списком становится видимым для чипов AVR, которые до- пускают самопрограммирование (Рис. 6.10). Эта опция позволяет выбрать тип сгенерированного кода: — Application (Приложение); — Boot Loader — Size: хххх words (Загрузчик — Размер: хххх слов). Clock: |4,000000 MHz Boot Loader • Size:512wor< Boot Loader - Size: 1024word$ Boot Loader Size:2048words Boot Loader - Size:4096word$ 6.2.2. Закладка External SRAM (Внешнее SRAM) Для чипов AVR, которые допускают подключение внешнего SRAM (ОЗУ), по- является закладка External SRAM (внешнее SRAM) (Рис. 6.11). На этой закладке можно задать следующие опции внешнего SRAM: • External SRAM size (Размер внешнего SRAM) — в этом окошке с выпадаю- щим списком можно определить размер внешней памяти. • External SRAM Wait State (Состояние ожидания внешнего SRAM) — вы- брав эту опцию, можно включить дополнительные состояния ожидания при доступе к внешнему SRAM.
6.2. Закладки «381 Регистр управления микроконтроллером MCUCR в коде начальной инициа- лизации конфигурируется автоматически, согласно этим установочным парамет- рам. Для чипов AVR, которые допускают разделение внешнего SRAM на две стра- ницы (например, ATmegal61), закладка External SRAM (Внешнее SRAM) будет выглядеть примерно так, как показано на Рис. 6.12. Рис, 6.11. Закладка External SRAM (Внешнее SRAM). Рис. 6.12. Закладка External SRAM (Внешнее SRAM) для чипов AVR, которые допускают раз- деление внешнего SRAM на две страницы. На этой закладке можно задать следующие опции внешнего SRAM: • External SRAM page configuration (Страничная конфигурация внешнего SRAM) — эта опция позволяет выбрать адреса разбиения для двух страниц внешнего SRAM; • Lower wait states (Состояние ожидания для нижней страницы) — эта опция с выпадающим списком определяет состояние ожидания, которое вставля- ется в течение доступа к нижней странице внешнего SRAM; • Upper wait states (Состояние ожидания для верхней страницы) — эта опция с выпадающим списком определяет состояние ожидания, которое вставля- ется в течение доступа к верхней странице внешнего SRAM. Регистры MCUCR и EMCUCR микроконтроллера в коде начальной инициа- лизации конфигурируются автоматически, согласно этим установочным пара- метрам.
382 Глава 6. Автоматический генератор программ CodeWizardAVR 6.2.3. Закладка Ports (Порты) Эта закладка (Рис. 6.13) позволяет в CodeWizardAVR определить конфигура- цию каждого порта ввода/вывода. Можно выбрать порт, который требуется сконфигурировать, выбрав соответс- твующую закладку PORT х (Порт х). Щёлкнув на соответствующий Data Direction bit (Бит направления данных), можно сделать вывод чипа выходом (Out) или вхо- дом (In). Регистр DDRx будет инициализирован согласно этим установочным парамет- рам. Щёлкнув на соответствующий бит в колонке Pullup/Output Value bit (Значение бита подтягивания/выходного), можно установить следующие опции: • если вывод является входом (In), он может быть установлен в третье состо- яние (Т) или иметь внутренний подтягивающий резистор (Р), подключён- ный к плюсу источника питания (Рис. 6.14); • если вывод является выходом (Out), его значение можно первоначально ус- тановить в 0 или 1 (Рис. 6.15). Регистр PORTx будет инициализирован согласно этим установочным пара- метрам. Рис. 6.13. Закладка Ports (Порты). Рис. 6.14. Установка опций для выводов, установленных входами (In).
6.2. Закладки 383 Рис. 6.15. Установка опций для выводов, установленных выходами (Out). 6.2.4. Закладка External IRQ (Внешнее прерывание) Эта закладка (Рис. 6.16) позволяет определить конфигурацию внешних пре- рываний. Отметив соответствующее окошко INTx Enabled (Разрешить прерывание х), можно разрешить соответствующее внешнее прерывание. Если чип AVR поддер- живает эту характеристику, то появится окошко Mode (Способ) с выпадающим списком, в котором можно выбрать способ инициирования прерывания: • Low level (Низкий уровень) — прерывание будет инициировано по низкому уровню на соответствующем входе; • Falling Edge (Спадающий фронт) — прерывание будет инициировано по спадающему фронту на соответствующем входе; • Rising Edge (Нарастающий фронт) — прерывание будет инициировано по нарастающему фронту на соответствующем входе. Для каждого разрешённого внешнего прерывания CodeWizardAVR определит программу обслуживания прерывания extintxisr, где х — номер внешнего пре- рывания. Для некоторых устройств, например ATmegal69V/L, закладка External IRQ (Внешнее прерывание) может предоставить дополнительные опции (Рис. 6.17). Если выбрать окошко Any change on I/O pins PCINTx (Любое изменение на ли- ниях ввода/вывода PCINTx), то появится дополнительное окошко Pin Change
384 Глава 6. Автоматический генератор программ CodeWizardAVR Рис. 6.16. Закладка External IRQ (Внешнее прерывание). Рис. 6.17. Закладка External IRQ (Внешнее прерывание) для ATmega 169V/L. Interrupt Enable (Изменение вывода разрешения прерывания), в котором можно определить, какой из выводов PCINTx I/O будет инициировать внешнее преры- вание. Для этих прерываний программы обслуживания прерывания будут pin_change_isrO для PCINT0-7 и pin_change_isrl для PCINT8-15. 6.2.5. Закладка Timers (Таймеры) Эта закладка (Рис. 6.18) позволяет определить конфигурацию таймеров/счёт- чиков. Количество закладок Timer х (Таймер х) на закладке Timers (Таймеры) зависит от типа используемого микроконтроллера. Закладка Timer 0 (Таймер 0) На закладке Timer 0 (Таймер 0) (Рис. 6.19) имеются следующие опции: • Clock Source (Источник тактовой частоты) определяет источник импульсов тактовой частоты для таймера/счётчика 0. Здесь в выпадающем списке (Рис. 6.20) можно выбрать: — System Clock (Системная тактовая частота) — таймер будет тактировать- ся тактовой частотой, на которой работает чип; — ТО pin Falling Edge (Вывод ТО спадающий фронт) — таймер будет такти- роваться от внешнего генератора, подключённого к выводу ТО чипа, по спадающему фронту;
6.2. Закладки 385 — ТО pin Rising Edge (Вывод TO нарастающий фронт) — таймер будет такти- роваться от внешнего генератора, подключённого к выводу ТО чипа, по нарастающему фронту. Рис. 6.18. Закладка Timer 0 (Таймер 0) на закладке Timers (Таймеры). • Clock Value (Значение тактовой частоты) определяет тактовую частоту для таймера/счётчика 0 при условии, что в качестве источника используется системная тактовая частота (см. предыдущую опцию). Здесь в выпадающем списке (Рис. 6.20) можно выбрать значение, зависящее от значения сис- темной тактовой частоты. По сути дела здесь задаётся коэффициент деле- ния предварительного делителя тактовой частоты таймера/счётчика 0. • Mode (Режим) определяет режим функционирования таймера/счётчика 0 (обычный режим, режим ШИМ и т. д. в зависимости от используемого чи- па). Подробнее см. описание на используемый микроконтроллер. • Outp. А (Выход А) определяет функции выхода совпадения А таймера/счёт- чика 0 и зависит от режима его функционирования. Здесь в выпадающем списке (Рис. 6.21) можно выбрать следующие функции: — Disconnected (Отключён); — Toggle on compare match (Переключаться при совпадении); — Clear on compare match (Сбрасываться при совпадении); — Set on compare match (Устанавливаться при совпадении). • Outp. В (Выход В) определяет функции выхода совпадения В таймера/счёт- чика 0 и зависит от режима функционирования. Здесь в выпадающем спис- ке (Рис. 6.22) можно выбрать следующие функции: — Disconnected (Отключён);
386 Гшва 6. Автоматический генератор программ Code WizardA VR Й CodeWizaidAVR - untitled.cwp |X | File Help US ART | Analog Comparator | ADC | SPI I2C | 1 Wire | 2Wire(l2C) LCD | Bit-Banged | Project Information Chip ] Ports | External IRQ Timers Timer 0 | Timer 1 | Timer 2 | Watchdog ] Clock Source: [ System Clock Clock Value: JO pin Falling Edge Mode: | Normal Outp. A:[ Disconnected Outp. В :| Disconnected j»] Г“ Overflow Interrupt Г Compare Match A Interrupt Г Compare M atch В I nterrupt Puc. 6.19. Выбор опции Clock Source (Источник тактовой частоты) для Таймера 0. Рис. 6.20. Выбор опции Clock Value (Значение тактовой частоты) для таймера/счётчика 0. Рис. 6.21. Выбор опции Outp. А (Выход А) для таймера/счётчика 0. Рис. 6.22. Выбор опции Outp. В (Выход В) для таймера/счётчика 0.
6.2. Закладки 387 — Toggle on compare match (Переключаться при совпадении); — Clear on compare match (Сбрасываться при совпадении); — Set on compare match (Устанавливаться при совпадении). • Overflow Interrupt (Прерывание по переполнению) определяет, должно ли быть сгенерировано прерывание при переполнении таймера/счётчика О или нет. • Compare Match A Interrupt (Прерывание по совпадению А) определяет, должно ли быть сгенерировано прерывание по совпадению А тайме- ра/счётчика 0 или нет. • Compare Match В Interrupt (Прерывание по совпадению В) определяет, должно ли быть сгенерировано прерывание по совпадению В тайме- ра/счётчика 0 или нет. • Timer Value (Значение таймера) определяет начальное значение тайме- ра/счётчика 0 при запуске. В этом окошке следует ввести требуемое началь- ное значение таймера/счётчика 0 в шестнадцатеричном виде. • Compare А (Совпадение А) определяет начальное значение выходного ре- гистра совпадения А таймера/счётчика 0. В этом окошке следует ввести требуемое начальное значение регистра совпадения А в шестнадцатерич- ном виде. • Compare В (Совпадение В) определяет начальное значение выходного ре- гистра совпадения В таймера/счётчика 0. В этом окошке следует ввести требуемое начальное значение регистра совпадения В в шестнадцатерич- ном виде. Если используются прерывания таймера/счётчика 0, то CodeWizardAVR опре- делит следующие программы обслуживания прерывания: • timerO_ovf_isr — для переполнения таймера/счётчика 0. • timerO_compa_isr — для выхода по совпадению А таймера/счётчика 0. • timerO compb isr — для выхода по совпадению В таймера/счётчика 0. Обратите внимание, что в зависимости от используемого чипа AVR неко- торых из этих опций может не быть. За более подробной информацией об- ратитесь к описанию (datasheet) соответствующего чипа от Atmel (см. Команда Help -> AVR Data Sheets (Помощь -> Описания AVR)). Закладка Timer 1 (Таймер 1) Закладка Timer 1 (Таймер 1) представлена на Рис. 6.23. Здесь имеются следующие опции: • Clock Source (Источник тактовой частоты) определяет источник импульсов тактовой частоты для таймера/счётчика 1. Здесь в выпадающем списке (Рис. 6.24) можно выбрать: — System Clock (Системная тактовая частота) — таймер будет тактировать- ся частотой, на которой работает чип; — Т1 pin Falling Edge (Вывод Т1 спадающий фронт) — таймер будет такти- роваться от внешнего генератора, подключённого к выводу Т1 чипа, по спадающему фронту;
388 Глава 6. Автоматический генератор программ Code WizardA VR Рис. 6.23. Закладка Timer 1 (Таймер 1) на закладке Timers (Таймеры). Рис. 6.24. Выбор опции Clock Source (Источник тактовой частоты) для таймера/счётчика 1. — Т1 pin Rising Edge (Вывод Т1 нарастающий фронт) — таймер будет такти- роваться от внешнего генератора, подключённого к выводу Т1 чипа, по нарастающему фронту. • Clock Value (Значение тактовой частоты) определяет тактовую частоту для таймера/счётчика 1 при условии, что в качестве источника используется системная тактовая частота (см. предыдущую опцию). Здесь в выпадающем списке (Рис. 6.25) можно выбрать значение, зависящее от значения сис- темной тактовой частоты. По сути дела здесь задаётся коэффициент деления предварительного дели- теля тактовой частоты таймера/счётчика 1; • Mode (Режим) определяет режим функционирования таймера/счётчика 1 (обычный режим, режим ШИМ и т. д. в зависимости от используемого чи- па). Подробнее см. описание на используемый микроконтроллер; • Out. А (Выход А) определяет функции выхода сравнения А таймера/счётчи- ка 1 и зависит от режима функционирования. Здесь в выпадающем списке (Рис. 6.26) можно выбрать следующие функции: — Discon. (Отключён); — Toggle (Переключаться при совпадении); — Clear (Сбрасываться при совпадении); — Set (Устанавливаться при совпадении). • Out. В (Выход В) определяет функции выхода сравнения В таймера/счётчи- ка 1 и зависит от режима функционирования. Здесь в выпадающем списке (Рис. 6.27) можно выбрать следующие функции:
6.2. Закладки 389 Рис. 6.25. Выбор опции Clock \blue (Значение тактовой частоты) для таймера/счётчика 1. Рис. 6.26. Выбор опции Out. А (Выход А) для таймера/счётчика 1. — Discon. (Отключён); — Toggle (Переключаться при совпадении); — Clear (Сбрасываться при совпадении); — Set (Устанавливаться при совпадении). • Out. С (Выход С) определяет функции выхода сравнения С таймера/счёт- чика 1 и зависит от режима функционирования. Здесь в выпадающем спис- ке (Рис. 6.28) можно выбрать следующие функции: — Discon. (Отключён); — Toggle (Переключаться при совпадении); — Clear (Сбрасываться при совпадении); — Set (Устанавливаться при совпадении). • Input Capt. определяет фронт, по которому инициализируется захват тайме- ра/счётчика 1, и должно ли быть использовано подавление шума. — Если поставить галочку в опции Rising Edge, то захват будет инициали- зироваться по нарастающему фронту, если убрать — по спадающему. — Если поставить галочку в опции Noise Cancel, то подавление шума будет включено, если убрать — отменено. • Interrupt on (Включить прерывание) определяет, должно ли быть сгенери- ровано прерывание таймера/счётчика 1 по переполнению (Timer 1 Overflow), по захвату входа (Input Capture) и по совпадению X (Compare X Match), где X — А, В или С. Чтобы прерывание было сгенерировано, следу- ет установить галочку в соответствующей опции.
390 Глава 6. Автоматический генератор программ Code WizardA VR Рис. 6.27. Выбор опции Out. В (Выход В) для таймера/счётчика 1. Рис. 6.28. Выбор опции Out. С (Выход С) для таймера/счетчика 1. • Value (Значение) определяет начальное значение таймера/счётчика 1 при запуске. В этом окошке следует ввести требуемое начальное значение тай- мера/счётчика 1 в шестнадцатеричном виде. • Inp. Capture (Вход захвата) определяет начальное значение регистра захвата таймера/счётчика 1. В этом окошке следует ввести требуемое начальное значение регистра захвата в шестнадцатеричном виде. • Comp. А, В и С (Совпадение А, В и С) определяет начальное значение вы- ходных регистров совпадения А, В и С таймера/счётчика 1. В этих окошках следует ввести требуемые начальные значения регистров совпадения в шес- тнадцатеричном виде. Если используются прерывания таймера/счётчика 1, то CodeWizardAVR опре- делит следующие программы обслуживания прерывания: • tiinerl_ovfjsr — для переполнения таймера/счётчика 1. • timerl_comp_isr или timerl_compa_isr, timerI compb isr и timerl_copmc_isr — для выхода по совпадению таймера/счётчика 1. • timerl_capt_isr — для захвата по входу таймера/счётчика 1. Обратите внимание, что в зависимости от используемого чипа AVR неко- торых из этих опций может не быть. За более подробной информацией об- ратитесь к описанию (datasheet) соответствующего чипа от Atmel (см. Команда Help —> AVR Data Sheets (Помощь Описания AVR)).
6.2. Закладки 391 Закладка Timer 2 (Таймер 2) Закладка Timer 2 (Таймер 2) представлена на Рис. 6.29. Рис. 6.29. Закладка Timer 2 (Таймер 2). Здесь имеются следующие опции: • Clock Source (Источник тактовой частоты) определяет источник импульсов тактовой частоты для таймера/счётчика 2. Здесь в выпадающем списке (Рис. 6.30) можно выбрать: — System Clock (Системная тактовая частота) — таймер будет тактировать- ся тактовой частотой, на которой работает чип; — Т2 pin Falling Edge (Вывод Т2 спадающий фронт) — таймер будет такти- роваться от внешнего генератора, подключённого к выводу Т2 чипа, по спадающему фронту; — Т2 pin Rising Edge (Вывод Т2 нарастающий фронт) — таймер будет такти- роваться от внешнего генератора, подключённого к выводу Т2 чипа, по нарастающему фронту. • Clock Value (Значение тактовой частоты) определяет тактовую частоту для таймера/счётчика 2 при условии, что в качестве источника используется системная тактовая частота (см. предыдущую опцию). Здесь в выпадающем списке (Рис. 6.31) можно выбрать значение, зависящее от значения сис- темной тактовой частоты. Здесь задаётся коэффициент деления предварительного делителя тактовой частоты таймера/счётчика 2.
392 Глава 6. Автоматический генератор программ CodeWizardAVR • Mode (Режим) определяет режим функционирования таймера/счётчика 2 (обычный режим, режим ШИМ и т. д. в зависимости от используемого чи- па). Подробнее см. описание на используемый микроконтроллер. Ф CodeWizardAVR untitled.cwp |Х| File Help Analog Comparator ] ADC | SPI | I2C 1 Wire | 2Wire(l2C) | LCD Bit-Banged | Project Information Chip | External SRAM | Ports External IRQ Timers | USARTO | USART1 T2 pin Falling Edge Clock Source: | System Clock Clock Value: Mode: P^uTp2Jin.rRiangEdge Output: J Disconnected Г Overflow Interrupt F Compare Match Interrupt Рис. 6.31. Выбор опции Clock \&1ие(3начение тактовой частоты) для таймера/счётчика 2. Puc. 6.30. Выбор опции Clock Source (Источник тактовой частоты) для таймера/счётчика 2. • Output (Выход) определяет функции выхода совпадения таймера/счётчика 2 и зависит от режима функционирования. Здесь в выпадающем списке (Рис. 6.32) можно выбрать следующие функции: — Disconnected (Отключён); — Toggle on compare match (Переключаться при совпадении); — Clear on compare match (Сбрасываться при совпадении); — Set on compare match (Устанавливаться при совпадении). • Overflow Interrupt (Прерывание по переполнению) определяет, должно ли быть сгенерировано прерывание при переполнении таймера/счётчика 2. • Compare Match Interrupt (Прерывание по совпадению) определяет, должно ли быть сгенерировано прерывание по совпадению таймера/счётчика 2. • Timer Value (Значение таймера) определяет начальное значение тайме- ра/счётчика 2 при запуске. В этом окошке следует ввести требуемое началь- ное значение таймера/счётчика 2 в шестнадцатеричном виде. • Compare (Совпадение) определяет начальное значение выходного регистра совпадения таймера/счётчика 2. В этом окошке следует ввести требуемое начальное значение регистра совпадения в шестнадцатеричном виде. Если используются прерывания таймера/счётчика 2, то CodeWizardAVR опре- делит следующие программы обслуживания прерывания:
6.2. Закладки 393 • timer2_ovf_isr — для переполнения таймера/счётчика 2; • timer2_comp_isr — для выхода по совпадению таймера/счётчика 2. Обратите внимание, что в зависимости от используемого чипа AVR неко- торых из этих опций может не быть. За более подробной информацией об- ратитесь к описанию (datasheet) соответствующего чипа от Atmel (см. Команда Help ->AVR Data Sheets (Помощь —> Описания AVR)) Й CodeWizaidAVR - untitled.cwp lX| Disconnected File Help Analog Comparator | ADC | SPI | I2C 1 Wire | 2 Wire (12C) | LCD Bit-Banged | Project Information Chip | External SRAM | Ports External IRQ Timers | USARTO | USART1 Clock Source: |System Clock Clock Value: [ Timer'2 Mode: [Normal top=FFh Output: | D isconnected T oggle on compare match Г Ove Clear on compare match Set on c are match Puc. 6.32. Выбор опции Output (Выход) для таймера/счётчика 2. Закладка Timer 3 (Таймер 3) Закладка Timer 3 (Таймер 3) представлена на Рис. 6.33. Здесь имеются следующие опции: • Clock Source (Источник тактовой частоты) определяет источник импульсов тактовой частоты для таймера/счётчика 3. Здесь в выпадающем списке (Рис. 6.34) можно выбрать: — System Clock (Системная тактовая частота) — таймер будет тактировать- ся частотой, на которой работает чип; — ТЗ pin Falling Edge (Вывод ТЗ спадающий фронт) — таймер будет такти- роваться от внешнего генератора, подключённого к выводу ТЗ чипа, по спадающему фронту; — ТЗ pin Rising Edge (Вывод ТЗ нарастающий фронт) — таймер будет такти- роваться от внешнего генератора, подключённого к выводу ТЗ чипа, по нарастающему фронту.
394 Глава 6. Автоматический генератор программ Code WizardA VR Рис. 6.33. Закладка Timer 3 (Таймер 3) на закладке Timers (Таймеры). Рис. 6.34. Выбор опции Clock Source (Источник тактовой частоты) для таймера/счётчика 3. • Clock \frlue (Значение тактовой частоты) определяет тактовую частоту для таймера/счётчика 3 при условии, что в качестве источника используется системная тактовая частота (см. предыдущую опцию). Здесь в выпадающем списке (Рис. 6.35) можно выбрать значение, зависящее от значения сис- темной тактовой частоты. Здесь задаётся коэффициент деления предварительного делителя тактовой частоты таймера/счётчика 3. • Mode (Режим) определяет режим функционирования таймера/счётчика 3 (обычный режим, режим ШИМ и т. д. в зависимости от используемого чи- па). Подробнее смотрите описание на используемый микроконтроллер. • Out. А (Выход А) определяет функции выхода сравнения А таймера/счётчи- ка 3 и зависит от режима функционирования. Здесь в выпадающем списке (Рис. 6.36) можно выбрать следующие функции: — Discon. (Отключён); — Toggle (Переключаться при совпадении); — Clear (Сбрасываться при совпадении); — Set (Устанавливаться при совпадении). • Out. В (Выход В) определяет функции выхода сравнения В таймера/счётчи- ка 3 и зависит от режима функционирования. Здесь в выпадающем списке (Рис. 6.37) можно выбрать следующие функции: — Discon. (Отключён); — Toggle (Переключаться при совпадении); — Clear (Сбрасываться при совпадении); — Set (Устанавливаться при совпадении).
6.2. Закладки 395 Й CodeWizardAVR - untitled.cwp fxj File Help Analog Comparator | ADC | SPI | I2C 1Wie | 2W«e(l2C) | LCD Bit-Banged | Project Information Chip | External SRAM | Ports External IRQ Timers | USARTO | USART1 Timer 1 | Timer 2 Timer3 [ Watchdoc < I ► Clock Source: | System Clock Clock Value: Timer 3 Stopped Mode: | Normal tc Out. A: | Discon Out. C:| Discon. 4000,000 kHz 500,000 kHz 62,500 kHz 15,625 kHz 3,906 kHz Input Capt. Noise Cancel Interrupt on:| f* Timer 3 0verflow Value: |5 h Inp. Capture: |F h Comp. A: [o h B:[o h C:|o h Рис. 6.36. Выбор опции Out. А (Выход А) для таймера/счётчика 3. Puc. 6.35. Выбор опции Clock Wue (Значение тактовой частоты) для таймера/счётчика 3. • Out. С (Выход С) определяет функции выхода сравнения С таймера/счёт- чика 3 и зависит от режима функционирования. Здесь в выпадающем спис- ке (Рис. 6.38) можно выбрать следующие функции: — Discon. (Отключён); — Toggle (Переключаться при совпадении); — Clear (Сбрасываться при совпадении); — Set (Устанавливаться при совпадении). • Input Capt. определяет фронт, по которому инициализируется захват тайме- ра/счётчика 3, и должно ли быть использовано подавление шума. — Если поставить галочку в опции Rising Edge, то захват будет инициали- зироваться по нарастающему фронту, если убрать — по спадающему. — Если поставить галочку в опции Noise Cancel, то подавление шума будет включено, если убрать — отменено. • Interrupt on (Включить прерывание) определяет, должно ли быть сгенери- ровано прерывание таймера/счётчика 3 по переполнению (Timer 3 Overflow), по захвату входа (Input Capture) и по совпадению X (Compare X Match), где X — А, В или С. Чтобы прерывание было сгенерировано, следу- ет установить галочку в соответствующей опции. • Value (Значение) определяет начальное значение таймера/счётчика 3 при запуске. В этом окошке следует ввести требуемое начальное значение тай- мера/счётчика 3 в шестнадцатеричном виде.
396 Глава 6. Автоматический генератор программ Code WizardA VR Рис. 6.37. Выбор опции Out. В (Выход В) для таймера/счётчика 3. Рис. 6.38. Выбор опции Out. С (Выход С) для таймера/счётчика 3. • Inp. Capture (Вход захвата) определяет начальное значение регистра захвата таймера/счётчика 3. В этом окошке следует ввести требуемое начальное значение регистра захвата в шестнадцатеричном виде. • Comp. А, В и С (Совпадение А, В и С) определяет начальное значение вы- ходных регистров совпадения А, В и С таймера/счётчика 3. В этих окошках следует ввести требуемые начальные значения регистров совпадения в шес- тнадцатеричном виде. Если используются прерывания таймера/счётчика 3, то CodeWizardAVR опре- делит следующие программы обслуживания прерывания: • timer3_ovf_isr — для переполнения таймера/счётчика 3; • timer3_comp_isr или timer3_compa_isr, timer3_compb_isr и timer3_copmc_isr — для выхода по совпадению таймера/счётчика 3; • timer3_capt_isr — для захвата по входу таймера/счётчика 3. Обратите внимание, что в зависимости от используемого чипа AVR неко- торых из этих опций может не быть. За более подробной информацией об- ратитесь к описанию (datasheet) соответствующего чипа от Atmel (см. Команда Help AVR Data Sheets (Помощь -» Описания AVR)). Закладка Watchdog (Сторожевой таймер) Закладка Watchdog (Сторожевой таймер) (Рис. 6.39) позволяет сконфигуриро- вать сторожевой таймер.
6.2. Закладки 397 Здесь имеется следующая опция: • Watchdog Timer Enabled (Разрешить сторожевой таймер). Если выбрать эту опцию, то сторожевой таймер будет активизирован (Рис. 6.40). Рис. 6.39. Закладка Watchdog (Сторожевой таймер) на закладке Timers (Таймеры). Рис. 6.40. Watchdog (Сторожевой таймер) активизирован. При этом появятся дополнительные опции: — Oscillator Prescaller (Коэффициент предварительного делителя частоты) — в этом поле можно задать коэффициент предварительного деления сис- темной тактовой частоты для сторожевого таймера. — Watchdog Timeout Interrupt (Прерывание по окончании периода стороже- вого таймера) — если выбрать эту опцию, то по окончании периода сто- рожевого таймера вместо сброса будет сгенерировано прерывание. Обратите внимание, что если Watchdog (Сторожевой таймер) разрешён, то разработчик должен самостоятельно добавить соответствующие ко- довые последовательности, чтобы его периодически сбрасывать. Пример: /* Эта часть программы сбросит Сторожевой таймер */ #asm("wdr") // сбрасываем Сторожевой таймер За более подробной информацией о сторожевом таймере (Watchdog) следует обратиться к описанию (datasheet) используемого чипа от Atmel (см. Команда Help AVR Data Sheets (Помощь Описания AVR)).
398 Глава 6. Автоматический генератор программ Code WizardA VR 6.2.6. Закладка UART или USART Для устройств, имеющих UART (Универсальный асинхронный приёмопере- датчик), в CodeWizardAVR имеется закладка UART (Рис. 6.41). Она позволяет оп- ределить конфигурацию UART Здесь имеются следующие опции: • Receiver (приёмник) — если выбрать эту опцию, то будет активизирован приёмник UART (Рис. 6.42). Рис. 6.41. Закладка UART (Универсальный асинхронный приёмопередатчик). Рис. 6.42. Активизация приёмника UART При этом появится дополнительная опция: — Rx Interrupt (Прерывание по окончании приёма) — эта опция позволяет выбрать режим работы приёмника. Если эта опция не выбрана (галочка снята), то приёмник будет работать в режиме опроса. Если выбрать эту опцию (установить галочку), то приёмник будет работать в режиме прерывания, управляющего цикли- ческим буфером (Рис. 6.43) При этом появится дополнительная опция Receiver Buffer (Буфер приём- ника), которая позволяет определить размер циклического буфера в ре- жиме управляющего прерывания. • Transmitter (Передатчик) — если выбрать эту опцию, то будет активизиро- ван передатчик UART (Рис. 6.44).
6.2. Закладки 399 Рис. 6.43. Выбор опции Rx Interrupt (Прерывание по окончании приема). Рис. 6.44. Активизация передатчика UART. При активизации передатчика UART появится дополнительная опция: — Тх Interrupt (Прерывание по окончании передачи), которая позволяет выбрать режим работы передатчика. Если эта опция не выбрана, то передатчик будет работать в режиме оп- роса. Если выбрать эту опцию (установить галочку), то передатчик бу- дет работать в режиме прерывания, управляющего циклическим буфе- ром (Рис. 6.45) При этом появится дополнительная опция Transmitter Buffer (Буфер передат- чика), которая позволяет определить размер циклического буфера в режиме уп- равляющего прерывания. Далее можно задать общие опции для приёмника и передатчика UART (Рис. 6.46). Скорость передачи в обоих режимах (приёмника и передатчика) можно определить, используя выпадающий список в окошке Baud Rate (Скорость передачи) (Рис. 6.47). CodeWizardAVR автоматически инициализирует регистр UBRR согласно уста- новленной скорости передачи и тактовой частоте чипа AVR. Для этих параметров будет вычислена и отображена Baud Rate Error (Ошибка скорости передачи). При подборе параметров связи следует стремиться получить Baud Rate Error (Ошибка скорости передачи), равную нулю! Выпадающий список Communications Parameters (Параметры связи) (Рис. 6.48) позволяет определить количество битов данных (Data), стоповых би- тов (Stop) и чётности (Parity), используемых для последовательной связи.
400 Глава 6. Автоматический генератор программ CodeWizardA VR Рис. 6.45. Выбор опции Тх Interrupt (Прерывание по окончании передачи). Рис. 6.46. Общие опции для приёмника и передатчика. Рис. 6.47. Выбор опции Baud Rate (Скорость передачи). Рис. 6.48. Выбор опции Communications Parameters (Параметры связи).
6.2. Закладки 401 Для устройств, имеющих USART (Универсальный синхронно-асинхронный приёмопередатчик), в CodeWizardAVR имеется закладка USART (Рис. 6.49). Она позволяет определить конфигурацию USART. Закладка USART отличается от закладки UART тем, что добавлен выпадаю- щий список Mode (Режим). Этот выпадающий список позволяет определить режимы связи: • Asynchronous (Асинхронный). • Synchronous Master (Синхронный ведущий) со сброшенным в 0 битом UCPOL регистра UCSRC. • Synchronous Master (Синхронный ведущий) с установленным в 1 битом UCPOL регистра UCSRC. • Synchronous Slave (Синхронный ведомый) со сброшенным в 0 битом UCPOL регистра UCSRC. • Synchronous Slave (Синхронный ведомый) с установленным в 1 битом UCPOL регистра UCSRC. Последовательная связь реализуется, используя стандартные функции вво- да/вывода getchar, gets, scanf, putchar, puts и printf (см. Стандартные функции вво- да/вывода). Для прерывания, управляющего последовательной связью, CodeWizardAVR автоматически переопределяет основные функции — getchar и putchar. Рис. 6.49. Закладка USART (Универсальный синхронно-асинхронный приёмопередатчик). Для программной реализации последовательной связи CodeVisionAVR ис- пользует специальные переменные и массивы. В Табл. 6.3 даётся их описание.
402 Глава 6. Автоматический генератор программ Code WizardA VR Таблица 6.3. Специальные переменные и массивы для последовательной связи Наименование Описание rxjbuffer Глобальный массив, который используется для реализации буфера приёмника rx_wr_index Глобальная переменная, которая является индексом массива rxjbuffer и используется для записи полученных символов в буфер rx_rd_index Глобальная переменная, которая является индексом массива rx_buffer и используется для чтения полученных символов из буфера функцией getchar rx_counter Глобальная переменная, которая содержит символы в массиве rxjbuffer, ещё не прочитанные функцией getchar rx_buffer_overflow Глобальная битовая переменная, которая устанавливается, если произошло переполнение буфера приёмника txjbuffer Глобальный массив, который используется для реализации буфера передатчика tx_wrjndex Глобальная переменная, которая является индексом массива txjbuffer и используется для записи в буфер символов, которые требуется передать tx_rdjndex Глобальная переменная, которая является индексом массива txjbuffer и используется для чтения из буфера символов, которые требуется передать функцией putchar tx_counter Глобальная переменная, которая содержит символы в массиве txjbuffer, ещё не переданные системой прерываний Для устройств с двумя UART будет представлено две закладки: UART0 и UART1 (Рис. 6.50). Для устройств с двумя USART будет представлено две закладки: USART0 и USART1 (Рис. 6.51). Рис. 6.50. Диалоговое окно CodeWizardAVR для устройств с двумя UART. Й CodeWizardAVR - untitled.cwp |Х| File Help External IRQ | TimerCjuSARfO| USArTT Analog Comparator | ADC**~|J>H | ’“12СГ 1 Wire | 2Wire(l2C) | LCD Bit-Banged ] Project Information [..J^EL____l| External SRAM I Ports Puc. 6.51. Диалоговое окно CodeWizardAVR для устройств с двумя USART. Опции конфигурации выбираются также, как описано выше. UARTO (USART0) использует обычные функции — putchar и getchar. В случае прерывания, управляющего буфером связи, UARTO (USART0) использует пере- менные и массивы, приведённые в Табл. 6.4.
6.2. Закладки 403 Таблица 6.4. Специальные переменные и массивы для UARTO (USART0). Наименование Описание rx_bufferO Глобальный массив, который используется для реализации буфера приёмника UARTO (USART0) rx_wr_indexO Глобальная переменная, которая является индексом массива rx_bufferO и используется для записи полученных символов в буфер rx_rd_indexO Глобальная переменная, которая является индексом массива rx_bufferO и используется для чтения полученных символов из буфера функцией getchar rx_counterO Глобальная переменная, которая содержит символы в массиве rx_bufferO, ещё не прочитанные функцией getchar rx_buffer_overflow Глобальная битовая переменная, которая устанавливается, если произошло переполнение буфера приёмника tx_bufferO Глобальный массив, который используется для реализации буфера передатчика UARTO (USART0) tx_wr_indexO Глобальная переменная, которая является индексом массива tx_bufferO и используется для записи в буфер символов, которые требуется передать tx_rd_mdexO Глобальная переменная, которая является индексом массива tx_bufferO и используется для чтения из буфера символов, которые требуется передать функцией putchar tx_counterO Глобальная переменная, которая содержит символы в массиве tx_bufferO, ещё не переданные системой прерываний UART1 (USART1) также использует обычные функции putchar и getchar. В слу- чае прерывания, управляющего буфером связи, UART1 (USART1) использует пе- ременные и массивы, приведённые в Табл. 6.5. Таблица 6.5. Специальные переменные и массивы для UART1 (USART1) Наименование Описание rx_bufferl Глобальный массив, который используется для реализации буфера приёмника UART1 (USART1) rx_wr_indexl Глобальная переменная, которая является индексом массива rx_bufferl и используется для записи полученных символов в буфер rx_rd_indexl Глобальная переменная, которая является индексом массива rx_bufferl и используется для чтения полученных символов из буфера функцией getchar rx_counterl Глобальная переменная, которая содержит символы в массиве rx_bufferl, ещё не прочитанные функцией getchar rx_buffer_overflow Глобальная битовая переменная, которая устанавливается, если произошло переполнение буфера приёмника tx_bufferl Глобальный массив, который используется для реализации буфера передатчика UART1 (USART1) tx_wr_indexl Глобальная переменная, которая является индексом массива tx_bufferl и используется для записи в буфер символов, которые требуется передать tx_rd_indexl Глобальная переменная, которая является индексом массива tx_bufferl и используется для чтения из буфера символов, которые требуется передать функцией putchar tx_counterl Глобальная переменная, которая содержит символы в массиве tx_bufferl, еще не переданные системой прерываний Весь последовательный ввод/вывод через UARTO (USART0) использует функ- ции, объявленные в заголовочном файле stdio.h.
404 Глава 6. Автоматический генератор программ CodeWizardA VR 6.2.7. Закладка Analog Comparator (Аналоговый компаратор) Эта закладка (Рис. 6.52) позволяет определить конфигурацию аналогового компаратора. Здесь имеется следующая опция: • Analog Comparator Enabled (Разрешить аналоговый компаратор) — эта оп- ция позволяет разрешить или запретить работу аналогового компаратора. Если эта опция не выбрана, то аналоговый компаратор будет запрещён, при этом питание от него будет отключено. Обычно это свойство использу- ется, если критично потребление процессора в холостом режиме и не тре- буется сброс микроконтроллера. Если выбрать эту опцию, то аналоговый компаратор будет активизирован (Рис. 6.53). Рис. 6.52. Закладка Analog Comparator (Аналоговый компаратор). Рис. 6.53. Analog Comparator (Аналоговый компаратор) активизирован. При этом появятся дополнительные опции: • Bandgap Voltage Reference (Подсоединить опорное напряжение) — если вы- брать эту опцию, то внутреннее опорное напряжение будет подано на по- ложительный вход аналогового компаратора. • Input Multiplexer (Входной мультиплексор) — если выбрать эту опцию, то к отрицательному входу аналогового компаратора будет подключён аналого- вый мультиплексор АЦП. • Analog Comparator Input Capture (Аналоговый компаратор — вход захвата) — если выбрать эту опцию, то функция захвата таймера/счётчика 1 будет уп- равляться выходом аналогового компаратора. При этом выход компаратора
6.2. Закладки 405 подключается непосредственно к схеме обработки захвата, предоставляя удобные средства подавления шума и выбора фронта, предусмотренные прерыванием по входу захвата таймера/счётчика 1. Если эта опция не выбрана, то схема захвата и компаратор будут разъединены. • Analog Comparator Output (Выход аналогового компаратора) — если выбрать эту опцию, то выход аналогового компаратора будет подключён к биту АСО регистра управления и состояния аналогового компаратора — ACSR. Если эта опция не выбрана, то выход аналогового компаратора будет от- ключён от бита АСО регистра ACSR. • Analog Comparator Interrupt (Прерывание по аналоговому компаратору) — эта опция определяет, должно ли генерироваться прерывание по измене- нию состояния выхода аналогового компаратора. Если выбрать эту опцию, то прерывание будет разрешено (Рис. 6.54). Й CodeWizardAVR - untitled,cwp fx| File Help SPI | I2C | 1 Wire | LCD LCD Controller Bit-Banged j Protect! nformation Chip | Ports | External IRQ | Timers USI | USART [Ajn^Comparaton) ADC P Analog Comparator Enabled Г" Bandgap Voltage Reference Г" Input Multiplexer Г Analog Comparator Input Capture 1“ Analog Comparator Output P Analog Comparator interrupt i Analog Comparator Interrupt Mode <• Interrupt on Output Toggle Г* Interrupt on Falling Output Edge C Interrupt on Rising Output Edge Г D isable D igital I nput В uf f er on Al N 0 Г Disable Digital Input Вuffer on AlN1 Puc. 6.54. Analog Comparator Interrupt (Прерывание по аналоговому компаратору) разрешено. При этом появится дополнительная опция Analog Comparator Interrupt Mode (Способ прерывания аналогового компаратора), в поле которой можно вы- брать тип изменения выхода, которое будет инициировать прерывание: — Interrupt on Output Toggle (Прерывание по переключению выхода); — Interrupt on Falling Output Edge (Прерывание по спадающему фронту на выходе); — Interrupt on Rising Output Edge (Прерывание по нарастающему фронту на выходе). • Disable Digital Input Buffer on AINO (Запретить вход цифрового буфера на AINO) — если выбрать эту опцию, то на выводе AIN0 будет запрещён вход цифрового буфера (Порта), таким образом, уменьшается энергопотребле-
406 Глава 6. Автоматический генератор программ CodeWizardAVR ние чипа. В этом случае соответствующий бит в регистре PINx всегда будет читаться как 0. • Disable Digital Input Buffer on AIN1 (Запретить вход цифрового буфера на AIN1) — если выбрать эту опцию, то на выводе AIN1 будет запрещён вход цифрового буфера (Порта), таким образом, уменьшается энергопотребле- ние чипа. В этом случае соответствующий бит в регистре PINx всегда будет читаться как 0. Если прерывание аналогового компаратора разрешено, то CodeWizardAVR оп- ределит программу обслуживания прерывания ana_comp_isr. Обратите внимание, что в зависимости от используемого чипа AVR неко- торых из этих опций может не быть. За более подробной информацией об- ратитесь к описанию (datasheet) соответствующего чипа от Atmel (см. Команда Help -> AVR Data Sheets (Помощь -> Описания AVR)) 6.2.8. Закладка ADC Для устройств, имеющих аналого-цифровой преобразователь (АЦП) (Analog-Digital Converter — ADC), в CodeWizardAVR имеется закладка ADC (Рис. 6.55). Она позволяет определить конфигурацию АЦП. Здесь имеется следующая опция: • ADC Enabled (Разрешить АЦП) — эта опция позволяет разрешить или за- претить работу аналого-цифрового преобразователя. Если эта опция не выбрана, то АЦП будет запрещён. Если выбрать эту опцию, то АЦП будет активизирован (Рис. 6.56). Й CodeWizardAVR - untitled.cwp |Х| Fde Help External IRQ | Timers | USARTO ] USART1 1 Wire | 2 Wire (12C) | LCD Bit-Banged j Project Information Chip | External SRAM | Ports Analog Comparator [ A^C.. 1| SPI | I2C F ADC Enabled Puc. 6.56. ADC активизирован. Puc. 6.55. Закладка ADC .
6.2. Закладки 407 При этом появятся дополнительные опции: • Use 8 bits (Использовать 8 битов) — если выбрать эту опцию, то можно бу- дет использовать только 8 наиболее значимых битов результата анало- го-цифрового преобразования. • High Speed (Высокая скорость) — если выбрать эту опцию, то будет исполь- зован режим высокоскоростного преобразования АЦП, но с более низкой точностью. • Volt. Ref. (Опорное напряжение) — этот выпадающий список (Рис. 6.57) позволяет определить источник опорного напряжения для АЦП. — AREF pin (Вывод AREF); - AVCC pin (Вывод AVCC); — Int., cap. on AREF (Внутренний, конденсатор на вывод AREF). Для некоторых чипов AVR вместо этой опции имеется опция ADC Bandgap (Подсоединить АЦП) (Рис. 6.58). Если выбрать эту опцию, то к АЦП будет подключён внутренний источник опорного напряжения. Рис. 6.57. Выбор опции Volt. Ref. (Опорное напряжение). Рис. 6.58. Опция ADC Bandgap (Подсоединить АЦП). • ADC Clock (Тактовая частота АЦП) — этот выпадающий список (Рис. 6.59) позволяет определить тактовую частоту АЦП. Возможные значения зави- сят от тактовой частоты чипа. Для некоторых чипов AVR доступна опция: • Auto Trigger Source (Источник автоматического инициирования) — этот выпадающий список (Рис. 6.60) позволяет определить событие, которое будет инициировать аналого-цифровое преобразование:
408 Пшваб^Автоматическийгенераторпрограш — None (Нет); — free Running (Свободный запуск); — Analog Comparator (Аналоговый компаратор); — External IRQ (Внешнее прерывание); — TimerO Compare Match (Совпадение таймера/счётчика 0); — TimerO Overflow (Переполнение таймера/счётчика 0); — Timerl Compare Match В (Совпадение В таймера/счётчика 1); — Timerl Overflow (Переполнение таймера/счётчика 1); — Timerl Capture Event (Захват таймера/счётчика 1). Рис. 6.59. Выбор опции ADC Clock (Тактовая частота АЦП). 0 CodeWizaidAVR untitled.cwp | X | File Help I2C | 1 Wire | 2Wire(l2C) LCD | Bit-Banged | Project Information Chip | Ports | External IRQ | Timers USART | Analog Comparator ADC | SPI P ADC Enabled F Use fi bits F ADC Interrupt F Highspeed Vo». Ref. :|aref pin 3 ДОС Clock: 1125.000 kHz 3 Auto Trigger Source: Free Running Analog Comparator External IRQO TimerO Compare Match TimerO Overflow Timerl Compare Match В Timerl Overflow — Puc, 6,60. Выбор источника инициирования аналого-цифрового преобразования. • ADC Interrupt (Прерывание АЦП) — эта опция определяет, должно ли гене- рироваться прерывание после завершения преобразования АЦП. Если выбрать эту опцию, то прерывание будет разрешено (Рис. 6.61). При этом появятся дополнительные опции: — ADC Noise Canceler (Подавление шума для АЦП) — если выбрать эту оп- цию, то в течение процесса преобразования чип будет установлен в ре- жим холостого хода (Idle mode); таким образом уменьшается шум, про- изводимый на АЦП цифровой электронной схемой; — Automatically Scan Inputs Enabled (Разрешить автоматическое сканирова- ние входов) — если разрешить эту опцию (поставить галочку в окошке Enabled (Разрешить)), то CodeWizardAVR сгенерирует код для сканирова- ния входной области АЦП и помещения результатов в массив. Начало и конец области определяются с помощью окошек First (Первый вход) и соответственно Last (Последний вход).
6.2. Закладки 409 Рис. 6.61. ADC Interrupt (Прерывание АЦП) разрешено. Если автоматическое сканирование входов запрещено (галочка снята), то можно выполнить одно аналого-цифровое преобразование, используя функцию unsigned int read_adc(unsigned char adcjnput) Эта функция возвращает результат аналого-цифрового преобразования для входа adc_input. Нумерация входов начинается с 0. Если прерывания разрешены, то вышеуказанная функция использует допол- нительную программу обслуживания прерывания adc_isr. Эта программа сохра- нит результат преобразования в глобальной переменной adc_data. Если разрешено автоматическое сканирование входов, то программа обслу- живания прерывания adc_isr сохранит результат преобразования в глобальном массиве adc_data. Программа пользователя должна читать результаты преобразо- вания из этого массива. Для некоторых чипов, типа ATmegal69V/L, имеется дополнительная опция: • Disable Digital Input Buffers (Запретить цифровые входы буферов) — эта оп- ция позволяет запретить цифровые входы буферов (Портов), используемые АЦП, за счёт чего уменьшается энергопотребление чипа. Это можно сде- лать, отметив соответствующее окошко Disable Digital Input Buffers (Запре- тить цифровые входы буферов) (Рис. 6.62). Если опция Automatically Scan Inputs (Автоматическое сканирование входов) разрешена, то соответствующие цифровые входы буферов (Портов) автоматичес- ки запрещаются для входов АЦП в диапазоне сканирования.
410 Глава 6. Автоматический генератор программ Code WizardA VR Disable Digital Input Buffets 17РГГГГ 01 2 3 4 5 Puc. 6.62. Запрет цифровых входов 1 и 2 буферов (Портов). Обратите внимание, что в зависимости от используемого чипа AVR неко- торых из этих опций может не быть. За более подробной информацией об- ратитесь к описанию (datasheet) соответствующего чипа от Atmel (см. Команда Help ->AVR Data Sheets (Помощь -> Описания AVR)) 6.2.9. Закладка SPI Эта закладка (Рис. 6.63) позволяет определить конфигурацию SPI-интерфейса. Рис. 6.63. Закладка SPI. Здесь имеется следующая опция: • SPI Enabled (Разрешить SPI) — эта опция позволяет разрешить или запре- тить работу SPI-интерфейса.
6.2. Закладки 411 Если эта опция не выбрана, то SPI-интерфейс будет запрещён. Если выбрать эту опцию, то внутренний SPI-интерфейс будет активизиро- ван (Рис. 6.64). Рис. 6.64. SPI-интерфейс активизирован. При этом появятся дополнительные опции: • Clock Rate х2 (Показатель тактовой частоты х2) — если выбрать эту опцию, то будет разрешено удвоение тактовой частоты SPI-интерфейса. • SPI Clock Rate (Тактовая частота SPI) — эта опция позволяет выбрать так- товую частоту SPI-интерфейса, используемую для последовательной пере- дачи. Возможные значения зависят от тактовой частоты чипа. • SPI Type (Тип SPI) — эта опция позволяет выбрать режим работы чипа AVR в SPI-интерфейсе: Master (ведущий) или Slave (ведомый). • Clock Phase (Фаза тактовой частоты) — эта опция позволяет выбрать пози- цию фронта стробирующего сигнала SCK относительно бита данных: Cycle Half (Половина цикла) или Cycle Start (Начало цикла). • Clock Polarity (Полярность тактовых импульсов) — эта опция позволяет вы- брать уровень тактовых импульсов в режиме холостого хода (Idle mode): Low (низкий) или High (высокий). • Data Order (Порядок данных) — эта опция позволяет определить порядок данных при последовательной передаче: MSB First (Первый старший байт) или LSB First (Первый младший байт). • SPI Interrupt (Прерывание SPI) — если выбрать эту опцию, то будет разре- шено прерывание от SPI-интерфейса.
412 Глава 6. Автоматический генератор программ Code WizardA VR Если прерывание SPI запрещено, то для передачи через SPI-интерфейс следует использовать соответствующие функции (см. Функции SPI). Если прерывание SPI разрешено, то следует использовать программу об- служивания прерывания spi_isr, объявленную CodeWizardAVR. 6.2.10. Закладка USI Для устройств, имеющих USI (Универсальный последовательный интер- фейс), в CodeWizardAVR имеется закладка USI (Рис. 6.65). Она позволяет опреде- лить конфигурацию USI. На закладке есть опция Mode, выпадающий список которой позволяет вы- брать режим работы USI (Рис. 6.66). Рис. 6.65. Закладка USI (Универсальный последовательный интерфейс). Рис. 6.66. Выбор режима работы USI. При этом возможны следующие режимы: — Disabled (Запрещён) — если выбрать эту опцию, то USI будет запрещён. — Three Wire (SPI) (Трёхпроводной (SPI)) — если выбрать эту опцию, то USI будет работать в режиме трёхпроводного SPI-интерфейса (Рис. 6.67). — Ъуо Wire (I2C) (Двухпроводной (12С)) — если выбрать эту опцию, то USI будет работать в режиме двух проводов (интерфейс 12С) (Рис. 6.68). — Two Wire (I2C), SCL low on ent. ovf. (Двухпроводной (I2C), SCL низкий при переполнении счётчика) — если выбрать эту опцию, то USI будет работать в режиме двух проводов (интерфейс 12С), причём сигнал SCL при переполнении счётчика будет переходить в низкий уровень.
6.2. Закладки 413 Рис. 6.67. Режим работы USI — Three Wire (SPI) (Трёхпроводной (SPI)). Рис. 6.68. Режим работы USI — Wo Wire (I2C) (Двухпроводной (12С)). При выборе этих опций появятся не- сколько дополнительных: — Clock (Тактовая частота) — этот выпадающий список позволяет за- дать тактовую частоту для сдвиго- вого регистра (Register — Reg) и четырёхбитного счётчика USI (Counter — Cnt) (Рис. 6.69). — USI Counter Overflow Interrupt (Прерывание по переполнению счётчика USI) — если выбрать эту опцию, то будет разрешено преры- вание по переполнению счётчика USI. При этом он будет считать количество полученных или пере- данных битов и генерировать пре- рывание по переполнению, когда передача данных завершена. Если эта опция выбрана, то CodeWizardAVR сгенерирует код для программы обслуживания прерывания usi_ovf_isr, которая будет выполнена при переполне- нии счётчика USI. Рис. 6.69. Опция Clock (Тактовая частота).
414 Гпава 6. Автоматический генератор программ Code WizardA VR — USI Start Condition Interrupt (Прерывание по стартовому условию USI) — если выбрать эту опцию, то CodeWizardAVR сгенерирует код для про- граммы обслуживания прерывания, которая будет выполнена при опре- делении стартового условия (Start) в шине 12С при работе USI в режиме TWo Wire (Двух проводов). 6.2.11. Закладка I2C Эта закладка (Рис. 6.70) позволяет определить конфигурацию шины 12С. Здесь имеются следующие опции: • I2C Port (Порт 12С) — этот выпадающий список позволяет определить, ка- кой порт будет использован для реализации шины 12С (Рис. 6.71). Если выбрать значение None (Нет), то шина 12С реализована не будет. Рис. 6.70. Закладка I2C. Рис. 6.71. Выбор порта для реализации шины 12С. • SDA Bit (Бит SDA) — этот выпадающий список позволяет определить, ка- кой бит порта будет использован для реализации сигнала SDA шины 12С (Рис. 6.72). • SCL Bit (Бит SCL) — этот выпадающий список позволяет определить, ка- кой бит порта будет использован для реализации сигнала SCL шины 12С (Рис. 6.73)
6.2. Закладки 415 Рис. 6.72. Выбор бита порта для реализации сигнала SDA. Рис. 6.73. Выбор бита порта для реализации сигнала SCL. Обратите внимание, что для сигналов SDA и SCL должны быть использо- ваны различные биты порта, В противном случае будет выдано сообщение об ошибке (Рис, 6,74), Рис. 6.74. Ошибка — бит SDA порта 12С конфликтует с битом SCL. Кроме того, на закладке I2C имеется ряд закладок, позволяющих CodeWizardAVR довольно просто сгенерировать код для устройств, имеющих ши- ну 12С и подключённых к чипу AVR.
416 Глава 6. Автоматический генератор программ Code WizardA VR Закладка LM75 Й CodeWizardAVR untitled.cwp [X | File Help External IRQ | Timers | USARTO | USART1 1 Wire | 2Wire(l2C) | LCD Bit-Banged | Project Information Chip | External SRAM | Ports Analog Comparator | ADC j SPI П2С_________j I2C Port: |PORTA SDA Bit |o SCL Bit: [7 "Vj LM75 | DS1621 | PCF8563] F< I ► P Enabled Address: |f< Г” Output Active High Temperature *C“ ---------- —। Hyrt:|75 |^|O.S.:|80 jjlfl | Если используется температурный дат- чик LM75, то следует выбрать закладку LM75 и опцию Enabled (Разрешить) (Рис. 6.75). При этом появятся дополнительные оп- ции: •Address (Адреса LM75) — эта опция позво- ляет определить 3 младших бита адресов ус- тройств LM75, подключённых к шине 12С. Максимум можно подключить 8 устройств LM75. Если подключить три устройства LM75 и выбрать, например, адреса 0, 2 и 3, то этим устройствам будут присвоены номера соот- ветственно 0, 2 и 3. При этом в коде, сгене- рированном CodeWizardAVR, появятся стро- ки для устройств LM75 с соответствующими номерами (Рис. 6.76). •Output Active High (высокий Активный уро- вень выхода) — эта опция позволяет опреде- лить активное состояние выхода O.S. LM75. Если установить галочку, то активным уров- нем выхода O.S. будет высокий, если снять — низкий. •Temperature °C (Температура °C) — эта оп- ция позволяет задать температуру гистере- зиса (окошко Hyst.) и температуру O.S. (окошко O.S.). Puc. 6.75. Закладка LM75 на закладке I2C. Рис. 6.76. Строки кода, сгенерированного CodeWizardAVR, для устройств LM75 с номерами 0, 2 и 3. Доступ к устройствам LM75 следует осуществлять через соответствующие функ- ции (см. Функции температурного датчика LM75 от National Semiconductor). За более подробной информацией об использовании температурного датчика LM75 обратитесь к его описанию (datasheet) на сайте производителя: http://www.national.com.
6.2. Закладки 417 Закладка DS1621 Й- CodeWizardAVR - untitled.cwp fx"| File Help External IRQ | Timers | USARTO | USART1 1 Wire | 2Wire(l2C) | LCD Bit-Banged ] Project Information Chip | External SRAM j Ports Analog Comparator | ADC | SPI I2C I2C Port: | PORTA -r| SDABit |o SCLBit:11 LM75 Г63162Т]| PCF85631 F< I > p Enabled Address: ||< 6 Г~ Output Active High - T emperature trigger *C----------। Low: [50 High:[55 Если используется термометр/термостат DS1621, то следует выбрать закладку DS1621 и выбрать опцию Enabled (Разрешить) (Рис. 6.77). При этом появятся дополнительные оп- ции: • Address (Адреса DS1621) — эта опция позволяет определить 3 младших бита адресов устройств DS1621, подключён- ных к шине 12С. Максимум можно под- ключить 8 устройств DS1621. Если подключить три устройства DS1621 и выбрать, например, адреса 1, 4 и 6, то этим устройствам будут присво- ены номера 1, 4 и 6. При этом в коде, сгенерированном CodeWizardAVR, поя- вятся строки для устройств DS1621 с со- ответствующими номерами (Рис. 6.78). • Output Active High (высокий Активный уровень выхода ) — эта опция позволя- ет определить активное состояние вы- хода DS1621. Если установить галочку, то активным уровнем выхода DS1621 будет высокий, если снять — низкий. • Temperature trigger °C (Температура пе- реключения °C) — эта опция позволяет задать нижнюю (Low) и верхнюю (High) температуру переключения для выхода Tout устройств DS 1621. Доступ к устройствам DS1621 следует осуществлять через соответствующие функции (см. Функции термометра/термостата DS1621 от Dallas Semiconductor). Puc. 6.77. Закладка DS1621 на закладкеI2C. Рис. 6.78. Строки кода, сгенерированного CodeWizardAVR, для устройств DS1621 с номерами 1, 4 и 6. За более подробной информацией об использовании термометра/термостата DS1621 обратитесь к его описанию (datasheet) на сайте производителя: http://www.dalsemi.com/.
418 Глава 6. Автоматический генератор программ Code WizardA VR Закладка PCF8563 Если используются часы реального времени (RTC) PCF8563, то следует вы- брать закладку PCF8563 и опцию Enabled (Разрешить) (Рис. 6.79). При этом появятся дополнитель- Рис. 6.79. Закладка PCF8563 на закладке I2C. ные опции: • CLKOUT (Выходная частота) — этот выпадающий список позволяет опре- делить частоту импульсов на выходе CLKOUT устройства PCF8563: OFF (ВЫКЛ), 1, 32, 1024 и 32768 Гц. • Alarm Interrupt (Прерывание по бу- дильнику) — если выбрать эту опцию, то будет разрешена генерация преры- вания на выводе INT при совпадении текущего времени и времени будиль- ника. • Timer (Таймер) — эта опция позволя- ет задать параметры Таймера PCF8563. Здесь имеются следующие дополни- тельные опции: — Clock (Тактовая частота) — этот выпадающий список позволяет задать тактовую частоту обратного счёта таймера PCF8563: OFF (ВЫКЛ), 1/60, 1,64 и 4096 Гц. — Int. Enabled (Разрешить прерыва- ние) — если выбрать эту опцию, то, когда значение обратного счё- та таймера станет равным 0, бу- дет сгенерировано прерывание. При этом появится дополнитель- ная опция: > INT Pulses (Импульсы INT) — если выбрать эту опцию, то, когда зна- чение обратного счёта таймера будет равно 0, вывод INT будет выда- вать короткие импульсы. При этом появится дополнительная опция Value (Значение), которая позволяет определить значение, которое будет загружено в Таймер при его перезагрузке, когда значение обрат- ного счёта достигнет 0. Доступ к устройствам PCF8563 следует осуществлять через соответствующие функции (см. Функции часов реального времени PCF8563 от Philips). За более подробной информацией об использовании часов реального време- ни PCF8563 обратитесь к его описанию (datasheet) на сайте производителя: http://www.semiconductors.philips.com.
6.2. Закладки 419 Закладка PCF8583 Если используются часы реального времени (RTC) PCF8583, то следует вы- брать закладку PCF8583 и опцию Enabled (Разрешить) (Рис. 6.80). При этом появится дополнительная опция Address (Адреса PCF8583) — эта опция позволяет определить младший бит адресов устройств PCF8583, подклю- чённых к шине 12С. Максимум можно подключить 2 устройства PCF8583. Если подключить два устройства PCF8583 и выбрать, например, адреса 0 и 1, то этим устройствам будут присвое- ны номера 0 и 1. При этом в коде, сгене- рированном CodeWizardAVR, появятся строки для устройств PCF8583 с соот- ветствующими номерами (Рис. 6.81). Доступ к устройствам PCF8583 следу- ет осуществлять через соответствующие функции (см. Функции часов реального времени PCF8583 от Philips). За более подробной информацией об использовании часов реального времени PCF8583 обратитесь к его описанию (datasheet) на сайте производителя: http://www.semiconductors.philips.com. Рис. 6.80. Закладка PCF8583 на закладке I2C. Рис. 6.81. Строки кода, сгенерированного CodeWizardAVR, для устройств PCF8583 с номерами 0 и 1. Закладка DS1307 Если используются часы реального времени (RTC) DS1307, то следует вы- брать закладку DS1307 и опцию Enabled (Разрешить) (Рис. 6.82). При этом появятся дополнительные опции: • OUT (Выход) — этот выпадающий список позволяет определить состояние (О или 1) вывода SQW/OUT DS1307 в случае, если выходные прямоуголь- ные импульсы запрещены, т. е. запрещена опция Square Wave Output (Вы- ходные прямоугольные импульсы).
420 Глава 6. Автоматический генератор программ CodeWizardAVR • Square Wave Output Enabled (Разрешить выходные прямоугольные импуль- сы) — если разрешить эту опцию, то на выводе SQW/OUT DS1307 будет разрешён прямоугольный импульсный сигнал. Частоту прямоугольных им- пульсов можно выбрать, используя выпадающий список Req. (Частота): 1, 4096, 8192 и 32768 Гц (Рис. 6.83). Рис. 6.82. Закладка DS1307 на закладке I2C. Рис. 6.83. Опция Square Wave Output Enabled (Разрешить выходные прямоугольные импульсы) разрешена. Доступ к устройствам DS1307 следует осуществлять через соответствующие функции (см. Функции часов реального времени DS1307 от Dallas Semiconductor). За более подробной информацией об использовании часов реального време- ни DS1307 обратитесь к его описанию (datasheet) на сайте производителя: http://www.dalsemi.com/. 6.2.12. Закладка 1 Wire Эта закладка (Рис. 6.84) позволяет определить конфигурацию шины 1-Wire. Здесь имеются следующие опции: • 1 Wire Port (Порт 1 Wire) — этот выпадающий список позволяет определить, какой порт будет использован для реализации шины 1-Wire (Рис. 6.85). Если выбрать значение None (Нет), то шина 1-Wire реализована не будет. • Data Bit (Бит данных) — этот выпадающий список позволяет определить, какой бит порта будет использован для шины 1-Wire (Рис. 6.86). Если используются температурные датчики DS1820/DS18S20, то следует вы- брать опцию Enabled (Разрешить) в поле DS1820/DS18S20 (Рис. 6.87).
6.2. Закладки 421 Рис, 6,84, Закладка 1 Wire. Рис. 6,85. Выбор порта для реализации шины 1-Wire. Рис. 6.87. Температурные датчики DS1820/DS18S20 разрешены. Рис, 6,86, Выбор бита порта для реализации шины 1-Wire.
422 Глава 6. Автоматический генератор программ Code WizardA VR Если используется несколько устройств DS1820/DS 18S20, подключённых к шине 1-Wire, то следует выбрать опцию Multiple Devices (Многочисленные уст- ройства). К шине можно подключить максимум 8 устройств DS1820/DS18S20. ROM-коды для этих устройств будут храниться в массиве dsl820_rom_codes. Доступ к устройствам DS 1820/DS18S20 следует осуществлять через соответс- твующие функции (см. Функции температурного датчика DS1820/DS18S20 от Dallas Semiconductor). За более подробной информацией об использовании температурных датчи- ков DS 1820/DS 18S20 обратитесь к их описанию (datasheet) на сайте производите- ля: http://www.dalsemi.com/. 6.2.13. Закладка 2 Wire (I2C) Эта закладка (Рис. 6.88) позволяет определить конфигурацию шины 2-Wire. Здесь имеется следующая опция: • 2 Wire Enabled (Разрешить 2-Wire) — эта опция позволяет разрешить или за- претить работу 2-Wire интерфейса. Если эта опция не выбрана, то 2-Wire интерфейс будет запрещён. Если выбрать эту опцию (поставить галочку), то 2-Wire интерфейс будет активизирован (Рис. 6.89). Рис. 6.88. Закладка 2 Wire (I2C). Рис. 6.89. 2-Wire интерфейс активизирован.
6.2. Закладки 423 При этом появятся дополнительные опции: • Generate Acknowledge Pulse (Генерировать импульсы подтверждения) — если выбрать эту опцию, то на шине 2-Wire будет генерироваться импульс АСК (импульс подтверждения), если выполнено одно из следующих условий: — получен s/л ve-адрес данного устройства; — получен General Call (Общий вызов) и выбрана опция (поставлена галоч- ка) General Call Recognition (Опознавание общего вызова); — получен байт данных в режиме master receiver (ведущий — приёмник) или slave receiver (ведомый — приёмник). Если опция Generate Acknowledge Pulse (Генерировать импульсы под- тверждения) не выбрана, то интерфейс 2-Wire чипа фактически отсоеди- нён от шины 2-Wire. Эта опция установит состояние бита TWEA регист- ра TWCR микроконтроллера. • Slave Address (Адрес ведомого) — в этом редактируемом окошке следует ввести я/яге-адрес последовательных устройств шины 2-Wire. Этот адрес должен быть определён в шестнадцатеричном виде, и он будет использо- ваться для инициализации битов 1 ...7 регистра TWAR микроконтроллера. • General Call Recognition (Опознавание общего вызова) — если выбрать эту опцию, то будет разрешено опознавание General Call (Общего вызова), пе- реданного по шине 2-Wire. Эта опция установит состояние бита TWGCE ре- гистра TWAR микроконтроллера. • Bit Rate (Битовый показатель) — эта опция позволяет определить макси- мальную частоту импульсов на линии SCL шины 2-Wire. Эта опция влияет на значение регистра TWBR микроконтроллера. Возможные значения этой опции зависят от тактовой частоты чипа. Как приёмник, так и передатчик при ожидании ответа длительно допуска- ют низкий уровень на линии SCL. Из-за этого реальная частота может быть ниже той, которая определена в этой опции. • 2 Wire Interrupt (Прерывание 2 Wire) — если выбрать эту опцию, то 2-Wire интерфейс будет генерировать прерывания. Эти прерывания будут обслу- живаться функцией twi_isr. 6.2.14. Закладка LCD Controller (Контроллер LCD) ATmega169 Эта закладка (Рис. 6.90) позволяет определить конфигурацию контроллера LCD, встроенного в чип ATmegal69V/L. Здесь имеются следующие опции: • LCD Enabled (Разрешить LCD) — эта опция позволяет разрешить или запре- тить работу LCD-контроллера, встроенного в чип ATmegal69V/L (Рис. 6.91). При этом появятся дополнительные опции: — LCD Low Power Waveform (Низкое импульсное энергопотребление LCD) — если выбрать эту опцию, то на выводы LCD будет подаваться импульсное питание. Это позволяет уменьшить энергопотребление LCD. — LCD frame Complete Interrupt (Прерывание по заполнению фрейма LCD) — если выбрать эту опцию, то контроллер LCD сгенерирует пре- рывание в начале нового фрейма. В режиме низкого импульсного энер- гопотребления это прерывание будет сгенерировано каждым вторым фреймом. Прерывание по заполнению фрейма будет обслуживаться функцией lcd_sofjsr.
424 Глава 6. Автоматический генератор программ CodeWizardA VR Рис. 6.90. Закладка LCD Controller (Контроллер LCD). Рис. 6.91. Контроллер LCD активизирован. — Duty Cycle (Служебный цикл) — этот выпадающий список позволяет вы- брать один из следующих служебных циклов: Static (Статический), 1/2, 1/3 или 1/4 (Рис. 6.92). — Bias (Смещение) — этот выпадающий список позволяет выбрать смеще- ние 1/3 или 1/2, если служебный цикл не Static (Статический). Иначе Bias (Смещение) является Статическим. Посмотрите документацию производителя LCD для выбора смещения. — Clock Source (Источник тактовой частоты) — этот выпадающий список (Рис. 6.93) позволяет выбрать источник тактовой частоты LCD: System (Так- товая частота чипа) или External (Внешняя асинхронная тактовая частота). — frame Rate (Показатель фрейма) — эта опция позволяет определить пока- затель фрейма LCD в герцах. LCD frame Rate Register (LCDFRR) (Регистр показателя фрейма LCD) инициализируется, основываясь на значении источника тактовой частоты и доступном показателе фрейма, который выбирается по возможности ближе к тому, который был определён. — frame Rate Error (Ошибка показателя фрейма) вычисляется, основыва- ясь на определённом показателе фрейма (frame Rate) и реально полу- ченном из регистра LCDFRR. — Used Segments (Используемые сегменты) — этот выпадающий список (Рис. 6.94) позволяет определить номер вывода порта, используемого для управления сегментом LCD. — Contrast Control (Управление контрастностью) — этот выпадающий спи- сок (Рис. 6.95) позволяет определить максимальное напряжение VLCD между выводами сегмента LCD и общим. Диапазон VLCD — 2.60...3.35 В.
6.2. Закладки 425 Рис. 6.92. Выбор опции Duty Cycle (Служебный цикл). Рис. 6.93. Выбор опции Clock Source (Источниктактовой частоты). SEG&23 SEGO 24 Рис. 6.94. Выбор опции Used Segments (Используемые сегменты). 2 80V 2 85V 2.90V 2 95V Рис. 6.95. Выбор опции Contrast Control (Управление контрастностью).
426 Глава 6. Автоматический генератор программ CodeWizardAVR За более подробной информацией об использовании встроенного LCD-конт- роллера обратитесь к описанию (datasheet) на микроконтроллер AVR ATmegal69 от производителя (см. Команда Help -> AVR Data Sheets (Помощь -> Описания AVR)). 6.2.15. Закладка LCD Эта закладка (Рис. 6.96) позволяет определить конфигурацию LCD. Здесь имеются следующие опции: • LCD Port (Порт LCD) — этот выпадающий список позволяет определить, какой порт будет использован для подключения LCD. Если выбрать значение None (Нет), то LCD подключён не будет. • Chars./Line (Символов на строку) — этот выпадающий список позволяет определить количество символов на строку дисплея. Этот параметр зависит от типа дисплея LCD. Это значение используется функцией led init. Ниже на закладке LCD приводится порядок подключения выводов LCD к вы- водам выбранного порта. Доступ к LCD-дисплею следует осуществлять через соответствующие функ- ции (см. LCD-функции). За более подробной информацией об использовании LCD-дисплеев обрати- тесь к их описанию (datasheet) на сайте соответствующего производителя. Рис. 6.96. Закладка LCD.
6.2. Закладки 427 6.2.16. Закладка Bit-Banged Эта закладка (Рис. 6.97) позволяет определить конфигурацию периферийных устройств, подключаемых по методу bit-banging. Здесь имеется всего одна закладка DS1302. Если используются часы реально- го времени (RTC) DS1302, то следует в выпадающем списке Port (Порт) выбрать порт, который будет использоваться для подключения DS1302. Если выбрать зна- чение None (Нет), то DS1302 подключён не будет. При выборе соответствующего порта для подключения часов реального вре- мени (RTC) DS1302 появятся дополнительные опции (Рис. 6.98): • I/O Bit (Биты ввода/вывода) — этот выпадающий список позволяет опре- делить, какой бит порта будет использован для сигнала ввода/вывода DS1302. • SCLK Bit (Бит SCLK) — этот выпадающий список позволяет определить, какой бит порта будет использован для сигнала SCLK DS1302. • /RST Bit (Бит /RST) — этот выпадающий список позволяет определить, ка- кой бит порта будет использован для сигнала /RST DS1302. Функция зарядки малым током DS1302 может быть активизирована установ- кой галочки в окошке Enabled в поле Trickle Charge (Зарядка малым током). Рис. 6.97. Закладка Bit-Banged. Рис. 6.98. Дополнительные опции для DS1302. При этом появятся дополнительные опции: • Diodes (Диоды) — этот выпадающий список позволяет определить количес- тво подключаемых диодов. • Charge Resistors (Зарядный резистор) — этот выпадающий список позволя- ет определить значение зарядного резистора.
428 Глава 6. Автоматический генератор программ Code WizardA VR Доступ к устройству DS1302 следует осуществлять через соответствующие функции (см. Функции часов реального времени DS1302 от Dallas Semiconductor). За более подробной информацией об использовании часов реального време- ни (RTC) DS1302 обратитесь к их описанию (datasheet) на сайте производителя: http://www.dalsemi.com/. 6.2.17. Закладка Project Information (Информация проекта) Эта закладка (Рис. 6.99) позволяет задать информацию, которая будет поме- щена в заголовке комментариев, расположенных в начале исходного файла Си, произведённого CodeWizardAVR. Здесь имеются следующие поля для заполнения: • Project Name (Название проекта) — здесь можно задать название разраба- тываемого проекта; • Version (Версия) — здесь можно задать номер текущей версии проекта; • Date (Дата) — здесь можно задать дату создания проекта; • Author (Автор) — здесь можно задать инициалы автора проекта; • Company (Компания) — здесь можно задать название компании-разработ- чика; • Comments (Комментарии) — здесь можно ввести какие-либо поясняющие комментарии. Пример заполненной формы информации о проекте показан на Рис. 6.100. Рис. 6.99. Закладка Project Information (Информация проекта). Рис. 6.100. Заполненная форма Project Information (Информация проекта).
6.3. Пример использования CodeWizardAVR 429 После того как CodeWizardAVR произведёт файл проекта Си, эта информация будет помещена в заголовке комментариев, расположенных в начале исходника (Рис. 6.101). Рис. 6.101. Информация о проекте, помещённая в начале исходника. 6.3. Пример использования CodeWizardAVR В качестве примера разработаем с помощью CodeWizardAVR программу, кото- рая будет формировать на выводе 1 Порта В меандр частотой 1 кГц. Этот сигнал можно использовать для звукового генератора, тактирования каких-либо уст- ройств и т. д. Выберем микроконтроллер AT90S2313 как один из самых простых и дешёвых и подключим его по типовой схеме (см. Рис. 4.1). Форма сигнала на выводе 1 Порта В (ножка 13 микросхемы) должна соответ- ствовать приведённой на Рис. 6.102. Рис. 6.102. Форма сигнала на выводе 1 Порта В, которую требуется получить. Частота сигнала 1 кГц, следовательно, период — 1 мс (1000) мкс, а полупериод — 0.5 мс (500 мкс). Таким образом, каждые 500 мкс логический уровень сигнала на вы- воде 1 Порта В должен меняться на противоположный: если была 1 — должен стать 0, если был 0 — должна стать 1. Для формирования временных промежутков в 500 мкс используем прерывания по переполнению таймера/счётчика 0. Таким образом, требуется так сконфигурировать таймер/счётчик 0, чтобы каждые 500 мкс он переполнялся и прерывал выполнение основной программы, а в программе обработки прерывания мы должны изменять на противоположный логический уровень сигнала на выводе 1 Порта В. Для формирования наиболее точных временных промежутков Atmel рекомен- дует использовать как можно большую тактовую частоту и как можно больший ко- эффициент предварительного делителя тактовой частоты таймера/счётчика. Выбе-
430 Глава 6. Автоматический генератор программ Code WizardA VR рем частоту кварца, который задаёт системную тактовую частоту, равную 4.096 МГц, т. к. число 4096 является степенью двойки, и после деления предварительным делителем тактовой частоты таймера/счётчика будут получаться целые значения. Запустим CodeVisionAVR (см. Запуск CodeVisionAVR). Создадим новый проект, для чего выберем соответствующую команду (см. Команда File -> New (Файл Новый)).В появившемся диалоговом окне Create New File (Создать новый файл) выберем тип (File ТУре) создаваемого файла — Project (Проект) (Рис. 6.103). После этого появится диалоговое окно Confirm (Подтверждение), в котором будет предложено для создания нового проекта использовать CodeWizardAVR (Ав- томатический генератор программ), щёлкнем по кнопке Yes (Да) (Рис. 6.104). С reate New File Confirm । File Type I C Source i 9 \ You are about to create a new project. Do you want to use the CodeWizardAVR? Puc. 6.104. Подтверждение использования CodeWizardAVR (Автоматический генератор программ). Puc. 6.103. Выбор типа создаваемого файла. В появившемся окне CodeWizardAVR (Автоматический генератор программ) на закладке Chip (Чип) выберем микроконтроллер и установим тактовую частоту чипа (Рис. 6.105). Рис. 6.105. Выбор микроконтроллера и его тактовой частоты.
6.3. Пример использования Code WizardA VR 431 Перейдём на закладку Timers (Таймеры) (Рис. 6.106). $ CodeWizaidAVR - untitled.cwp |Х| File Help UART | Analog Comparator | I2C | 1 Wire LCD | Bit-Banged ] Project Informatm Chip j Ports | External IRQ/j Timers Chip: |aT90S2313 3 Puc, 6,106, Выбор закладки Timers (Таймеры). На закладке Timer 0 (Таймер 0) в качестве источника тактовой частоты тайме- ра/счётчика 0 (Clock Source) выберем System Clock (Системная тактовая частота). В выпадающем списке Clock Value (Значение тактовой частоты) выберем по при- чинам, описанным выше, наименьшее значение, установив, таким образом, на- ибольший коэффициент деления предварительного делителя частоты тайме- ра/счётчика 0 (Рис. 6.107). •Й CodeWizaidAVR untitled.cwp [x| File Help UART | Analog Comparator | I2C | 1 Wire LCD | Bit-Banged ] Project Information Chip | Ports | External IRQ Timers Timer Value: |cT~ h Puc, 6,107, Выбор опций Clock Source (Источник тактовой частоты) и Clock \fclue (Значение тактовой частоты) таймера/счётчика 0. Теперь требуется определить начальное значение таймера/счётчика 0. Опять сделаем небольшие расчёты. Как указывалось выше, таймер/счётчик 0 должен переполняться и вызывать прерывание по переполнению каждые 500 мкс, т. е. пе- риод прерываний ТПр = 500 мкс.
432 Глава 6. Автоматический генератор программ CodeWizardAVR Тогда получается, что Тпр = То х (Nmax- No). Следовательно, No = Nmax-(Tnp/ То), где То — период тактовых импульсов таймера после предварительного делителя частоты, мкс. То = 1До, где f0 — тактовая частота таймера/счётчика 0, в нашем случае f0 = 4 кГц = 0.004 МГц; Nmax — максимальное значение таймера/счётчика 0 (Nmax = 256); No — начальное значение таймера/счётчика 0, которое требуется определить. Подставим все значения в предыдущую формулу и получим No = Nmax- (ТПР / То) = Nmax - (ТПР х f0) = 256 - (500 х 0.004) = 256 - 2 = 254. Если целого числа не получилось, то следует подобрать значение тактовой частоты (частоты кварцевого резонатора) и коэффициент деления предваритель- ного делителя тактовой частоты таймера/счётчика 0. Теперь на закладке Timer 0 (Таймер 0) разрешим прерывание по переполне- нию таймера/счётчика 0. Для этого выберем опцию Overflow Interrupt (Прерыва- ние по переполнению) и введём в окошке Timer Value (Значение таймера) в шест- надцатеричном виде рассчитанное нами начальное значение таймера/счётчика 0, учитывая, что 254 = OxFE. Окончательно закладка Timer 0 (Таймер 0) должна выглядеть так, как показа- но на Рис. 6.108. Рис. 6.108. Окончательная конфигурация таймера/счётчика 0.
6.3. Пример использования Code WizardAVR 433 Теперь перейдём на закладку Ports (Порты) (Рис. 6.109). © CodeWizardAVR - untitled.cwp [Х| File Help UART | Analog Comparator | I2C | 1 Wire LCD L-Brt-Benged | Project Information Chip / ] PortsixjVxternalIRQ Timers J Watchdog | TimerO Clock Source: [System Clock Puc. 6.109. Выбор закладки Ports (Порты). На закладке Port В (Порт В) сконфигурируем все биты Порта В как выходы со значением 0 (можно и со значением 1) (Рис. 6.110). Хотя в нашем примере это важно только для вывода 1, остальные биты Порта В могут быть сконфигурирова- ны произвольно. Рис. 6.110. Конфигурация Порта В. Остальные периферийные устройства нас в данном случае не интересуют — оставим их установленными по умолчанию. Осталось занести информацию о проекте; хотя это необязательно, но являет- ся признаком хорошего тона. Для этого перейдём на закладку Project Information (Информация проекта) (Рис. 6.111).
434 Глава 6. Автоматический генератор программ Code WizardA VR Рис. 6.11L Выбор закладки Project Information (Информация проекта). Занесём информацию в соответствующие поля, например, как показано на Рис. 6.112. Теперь всё готово для генерации проекта. Выберем команду File Generate, Save and Exit (Файл Генерировать, сохранить и выйти) (Рис. 6.113). Рис. 6.112. Занесение информации о проекте. Рис. 6.113. Выбор команды File -* Generate, Save and Exit (Файл -> Генерировать, сохранить и выйти). После выбора этой команды появится окно Save С Compiler Source File (Со- хранить исходный файл компилятора Си), в котором выберем директорию для сохранения всего проекта (Рис. 6.114).
6.3. Пример использования CodeWizardAVR 435 Рис, 6.114, Выбор директории для сохранения проекта в окне Save С Compiler Source File (Сохранить исходный файл компилятора Си). После выбора директории щёлкнем в окне правой кнопкой мыши и в выпада- ющем меню выберем команду Создать Папку для создания папки, в которой будет храниться наш проект, например, с именем «Frequency» (Рис. 6.115). Рис. 6.115. Переименование папки для сохранения проекта. После этого дважды щёлкнем по значку папки с именем «Frequency», чтобы открыть её. В поле Имя файла введём имя исходного файла проекта с расширени- ем с. и щёлкнем по кнопке Сохранить. Затем в последовательно появляющихся окнах Save С Compiler Project File (Сохранить файл проекта компилятора Си) и Save...As (Сохранить...Как) точно также введём и сохраним соответственно своё имя для файла проекта с расшире- нием .prj и для файла проекта CodeWizardAVR с расширением .cwp. Для примера всем файлам присвоено одно и то же имя, хотя это и не обяза- тельно.
436 Глава 6. Автоматический генератор программ CodeWizardAVR После этого CodeWizardAVR откроет окно исходного файла проекта со сгене- рированным кодом (Рис. 6.116). Рис. 6.116. Окно со сгенерированным кодом. Текст сгенерированного кода для нашего примера с русифицированными комментариями: /***************************************************** Эта программа была произведена Автоматическим программным генератором CodeWizardAVR Vl.24.lx Professional © Copyright 1998-2004 Pavel Haiduc, HP InfoTech s.r.l. http://www.hpinfotech.ro e-mail:officeOhpinfotech.ro Проект Версия Дата Автор Компания Комментарии Frequency 1.00 01.08.2006 Лебедев М.Б. Ноте Пример программы AT90S2313 Тип чипа Тактовая частота: 4.096000 МГц Модель памяти: Tiny Размер внешнего SRAM:0 Размер стека данных:32 ttinclude <90s2313.h> // программа обработки прерывания по переполнению таймера/счётчика 0
6.3. Пример использования Code WizardAVR 437 interrupt [TIMO_OVF] void timerO_ovf_isr (void) ( // Переинициализация значения таймера/счётчика О TCNT0=0xFE; // Здесь поместите ваш код ) // Здесь объявите ваши глобальные переменные void main(void) { // Здесь объявите ваши локальные переменные // Инициализация портов ввода/вывода // Инициализация Порта В // ФункцияОВыход Функция1=Выход Функция2=Выход ФункцияЗ=Выход // Функция4=Выход Функция5=Выход Функция6=Выход Функция?=Выход // Состояние0=0 Состояние1=0 Состояние2=0 Состояние3=0 // Состояние4=0 Состояние5=0 Состояниеб=0 Состояние7=0 PORTB=OxOO; DDRB=0xFF; // Инициализация Порта D // ФункцияО=Вход Функция!=Вход Функция2=Вход ФункцияЗ=Вход // Функция4=Вход Функция5=Вход Функцияб=Вход // СостояниеО=Т Состояние1=Т Состояние2=Т СостояниеЗ=Т // Состояние4=Т Состояние5=Т Состояниеб=Т (Т - третье состояние) PORTD=OxOO; DDRD=0x00; // Инициализация таймера/счётчика О // Источник тактовой частоты: системная тактовая частота // Значение тактовой частоты: 4.000 кГц TCCR0=0x05; TCNT0=0xFE; // Инициализация таймера/счётчика 1 // Источник тактовой частоты: системная тактовая частота // Значение тактовой частоты: таймер 1 остановлен // Режим: Normal top=FFFFh // ОС1 выход: откл. // Подавление шума: выкл. // Вход захвата по спадающему фронту TCCRlA=0x00; TCCRlB=0x00; TCNTlH=0x00; TCNTlL=0x00; OCRlH=OxOO; OCRlL=OxOO;
438 Глава 6. Автоматический генератор программ Code WizardA VR II Инициализация внешних прерываний // INTO: выкл. // INT1: выкл. GIMSK=0x00; MCUCR=0x00; // Инициализация прерываний таймеров/счётчиков TIMSK=0x02; // Инициализация аналогового компаратора // Аналоговый компаратор: выкл. // Аналоговый компаратор вход захвата таймером/счётчиком 1: выкл. // Выход аналогового компаратора: выкл. ACSR=0x80; // Разрешение глобальных прерываний #asm("sei") while (1) { // Здесь поместите ваш код }; } Таким образом, CodeWizardAVR сгенерировал своеобразную заготовку про- граммы, в которой выполнено подключение заголовочного файла соответствую- щего микроконтроллера, а периферийные устройства используемого микроконт- роллера сконфигурированы в соответствии с настройками пользователя на соот- ветствующих закладках в окне CodeWizardAVR. CodeWizardAVR обильно снабжает сгенерированный код соответствующими комментариями, подробно описывая, как сконфигурировано то или иное пери- ферийное устройство. Кроме того, CodeWizardAVR подсказывает разработчику, где следует поместить объявление глобальных и локальных переменных, код, ко- торый должен выполняться программой обработки прерывания, код основной программы и т. д. В нашем примере требуется, чтобы при возникновении прерывания по пере- полнению таймера/счётчика 0 логический уровень сигнала на выводе 1 Порта В менялся на противоположный. Для этого в программу обработки прерывания по переполнению таймера/счётчика 0 timerO_ovf_isr вставим строку PORTB.1=~PORTB.1; // логический уровень на выводе 1 Порта В меняем //на противоположный (см. Унарные операции) в то место, куда указывает CodeWizardAVR (Рис. 6.117).
6.3. Пример использования Code WizardA VR 439 Рис. 6.117. Вставка кода пользователя. Если требуется, чтобы между прерываниями микроконтроллер что-то делал, то следует вставить соответствующий код в бесконечный цикл while (1), расположен- ный в конце программы, в то место, куда указывает CodeWizardAVR (Рис. 6.118). Рис. 6.118. Место для вставки кода основной программы. Так как в нашем примере этого не требуется, то оставим это место пустым. Для последующей отладки нашего проекта в Atmel AVR Studio 4 следует сде- лать соответствующие настройки отладчика (см. Команда Settings —> Debugger (Настройки Отладчик)), а при конфигурировании проекта на закладке С Compiler (Компилятор Си) (см. Закладка С Compiler (Компилятор Си)) в опции File Output Format(s) (Формат(ы) выходных файлов) следует выбрать набор файлов, содержащий тип COF. После всех настроек и конфигураций выберем команду Project -> Compile (Проект -> Компилировать). Убедимся, что в появившемся после компиляции окне Information (Информация) сообщается об отсутствии ошибок и предупреж- дений, и щёлкнем по кнопке ОК (Рис. 6.119). Если сообщения об ошибках присутствуют, то ошибки следует устранить и пе- рекомпилировать проект заново. Выбираем команду Projects Make (Проект -> Построить). Убеждаемся, что и на закладке Assembler (Ассемблер) есть сообщение об отсутствии ошибок (Рис. 6.120). Проект готов. Теперь займёмся его отладкой. Выбрав команду Tools —> Debugger (Инструменты -> Отладчик), запустим отладчик Atmel AVR Studio 4. В появившемся окне Welcome to AVR Studio 4 (Добро пожаловать в AVR Studio 4) щёлкнем по кнопке Open (Открыть) (Рис. 6.121).
440 Глава 6. Автоматический генератор программ Code WizardA VR Рис. 6.119. Окно Information (Информация) с сообщением об отсутствии ошибок и предупреждений. Рис. 6.120. Закладка Assembler (Ассемблер) в окне Information (Информация) с сообщением об отсутствии ошибок. Рис. 6.121. Окно Welcome to AVR Studio 4 (Добро пожаловать в AVR Studio 4).
6.3. Пример использования Code WizardA VR 441 В появившемся окне Open SaveFile or ObjectFile (Открыть сохранённый или объектный файл) перейдём в папку нашего проекта, выберем соответствующий файл с расширением .cof и щёлкнем по кнопке Открыть (Рис. 6.122). Рис. 6.122. Окно Open SaveFile or ObjectFile (Открыть сохранённый или объектный файл). Далее щёлкая по соответствующим опциям, выберем Debug Platform (Плат- форма отладки) — AVR Simulator (Симулятор AVR), Device (Устройство) — AT90S2313 и щёлкнем по кнопке Finish (Финиш) (Рис. 6.123). Рис. 6.123. Выбор Debug Platform (Платформы Отладки) и Device (Устройства). После этого наш файл будет автоматически загружен в AVR Studio версии 4 и будет создан новый проект AYR Studio, связанный с нашим файлом СОЕ
442 Глава 6. Автоматический генератор программ CodeWizardAVR Если файл ранее уже открывался, то его название будет присутствовать в списке Recent projects (Последние проекты). Тогда можно просто дважды щёлк- нуть по его названию (Рис. 6.124). Рис. 6.124. Выбор открывавшегося ранее файла. В обоих случаях откроется окно AVR Studio с загруженным файлом (Рис. 6.125). В окне Workspace (Рабочая область) откроем интересующие нас пе- риферийные устройства, нажав на соответствующие знаки «+». Остальные пункты сделаем невидимыми, щёлкнув в этом окне правой кноп- кой мыши и выбрав в выпадающем меню команду Hide non-expanded (Скрыть не- развёрнутые) (Рис. 6.126). Обратите внимание, что частота процессора в симуляторе не соответствует ус- тановленной в нашем проекте. Выберем команду Debug -> AVR Simulator Options (Отладка -» Опции симуля- тора AVR). В открывшемся окне Simulator Options (Опции симулятора) введём требуемое значение частоты и щёлкнем по кнопке ОК (Рис. 6.127). Всё настроено — приступим к отладке. Жёлтая стрелка в окне с текстом про- граммы указывает на оператор, который будет выполняться следующим. В начале она указывает на оператор, который будет выполняться первым (Рис. 6.128). Установим курсор в строку #asm(sei), т. е. сразу после всех инициализаций пе- риферийных устройств, просто щёлкнув мышью в начало этой строки. В этой строке появляется мигающая вертикальная черта, указывающая место, куда уста- новлен курсор. Щёлкнем по кнопке Run to cursor (Выполнять до курсора) на па- нели инструментов. Программа начнёт выполняться и остановится в той строке, где установлен курсор (Рис. 6.129).
6.3. Пример использования CodeWizardAVR 443 Рис. 6.125. Окно AVR Studio с загруженным файлом. Рис. 6.126. Настройка окна Workspace (Рабочая область).
444 Глава 6. Автоматический генератор программ Code WizardA VR Рис. 6.127. Диалоговое окно Simulator Options (Опции симулятора). 1= D:\AVRTools\CodeVision_1_24_ void main(void) Input/Output Port <// Deciare your local v «**// Func7=0ut Funcb=0i Stated«0 фPORTB=0x00 ^*DDRB«0x,FF; // Func6=In Fun *// Stateb=T S PORTD»0x00: DDRD=0x0 Puc. 6.128. Начальное положение жёлтой стрелки.
6.3. Пример использования CodeWizardAVR 445 Рис. 6.129. Остановка выполнения программы в позиции курсора. В окне Workspace (Рабочая область) красным цветом выделяются все измене- ния, произошедшие за время выполнения программы: • в регистр направления данных DDRB Порта В во все биты были записаны 1, т. е. все выводы стали выходами; • в регистр PORTB во все биты были записаны нули, и, таким образом, на всех физических выводах (регистр PINB) Порта В установился уровень ло- гического 0; • в регистр маски прерывания от таймеров/счётчиков TIMSK в бит 1 была за- писана 1, т. е. разрешено прерывание по переполнению таймера/счётчика 0; • в регистр управления таймера/счётчика 0 TCCR0 в биты 0 и 2 были записа- ны 1, а в бит 1 — 0, т. е. задан коэффициент деления предварительного де- лителя тактовой частоты таймера/счётчика 0, равный 1024; • во флаговом регистре прерываний от таймеров/счётчиков TIFR в бите 1 за- писан 0, т. е. флаг переполнения таймера/счётчика 0 сброшен; • в сам таймер/счётчик 0 TCNT0 было записано число OxFE. Таким образом, инициализация Порта В и таймера/счётчика 0 выполнена верно, в соответствии с заданием. Щёлкнем по кнопке AutoStep (Автоматически пошагово) на панели инструмен- тов. Программа начинает пошагово выполняться (Рис. 6.130). Можно заметить, что основное время программа выполняет пустой цикл while. Во время выполнения программы содержимое таймера/счётчика 0 (регистр TCNT0) увеличивается. Пос- ле того как оно достигнет значения 255 (во всех битах — 1), при переходе в значение
446 Глава 6. Автоматический генератор программ Code WizardA VR О (во всех битах — 0) происходит прерывание, выполняется программа обработки прерывания, и программа возвращается к выполнению цикла while. После каждого выполнения программы обработки прерывания бит 1 Порта В переключается (см. регистры PORTB и PINB). Рис. 6.130. Автоматическое пошаговое выполнение программы. Рассмотрим подробнее программу обработки прерывания. Прервём выполнение программы, нажав на панели инструментов кнопку Break (Прервать) (Рис. 6.131). AVRStudio - D:\AVRTools\CodeVision_1_24_1cVlpHMepbi nporpaMM\Frequency\fre... |« j[P |[Х | Рис. 6.131. Прерывание выполнения программы командой Break (Прервать). Установим курсор в строку, начинающуюся с ключевого слова interrupt, т. е. в начало программы обработки прерывания. Щёлкнем на панели инструментов по кнопке Run to cursor (Выполнять до курсора). Программа начнёт выполняться и остановится там, где установлен курсор (в этой строке появляется жёлтая стрел- ка) (Рис. 6.132).
6.3. Пример использования CodeWizardAVR 447 Рис. 6.132. Состояние после прерывания по переполнению таймера/счетчика 0. Видно, что прерывание произошло, когда содержимое таймера/счётчика 0 пе- решло в 0. Щёлкнем на панели инструментов по кнопке Step Into (Пошагово с заходом в подпрограммы). Программа зайдет в подпрограмму обработки прерывания и ос- тановится на первом операторе (Рис. 6.133). Рис. 6.133. Заход в подпрограмму обработки прерывания.
448 Глава 6. Автоматический генератор программ CodeWizardAVR Щёлкнем ещё раз по кнопке Step Into (Пошагово с заходом в подпрограммы) и увидим, что в результате выполнения оператора TCNT0 = 0xFE в таймер/счётчик 0 (регистр TCNT0) записывается число OxFE. После этого осу- ществляется переход к следующему оператору (Рис. 6.134). Опять щёлкнем по кнопке Step Into (Пошагово с заходом в подпрограммы) и увидим, что в результате выполнения оператора PORTB.1 = -PORTB.1 в регистре PORTB Порта В содержимое бита 1 меняется на противоположное. После этого осуществляется завершение подпрограммы обработки прерывания (Рис. 6.135). Обратите внимание, что логический уровень на физическом выводе PINB.1 не изменился, он изменится только в следующем машинном цикле. Снова щёлкнем по кнопке Step Into (Пошагово с заходом в подпрограммы) и увидим, что логический уровень на физическом выводе PINB.1 меняется на про- тивоположный, а программа возвращается в цикл while (Рис. 6.136). Осталось измерить время между сменами логического уровня бита 1 регистра PORTB Порта В. Смены логического уровня на физическом выводе PINB.1 будут сдвинуты во времени на один машинный цикл, но промежуток между ними будет таким же. Установим курсор в строку с оператором PORTB. 1 = -PORTB. 1 в подпро- грамме обработки прерывания и щёлкнем на панели инструментов по кнопке Run to cursor (Выполнять до курсора). Программа начнёт выполняться и остано- вится там, где установлен курсор (в этой строке появляется жёлтая стрелка) (Рис. 6.137). В окне Workspace (Рабочая область) дважды щёлкнем по строке Stop Watch (Остановка часов), обнулив значение счётчика времени. Снова щёлкнем на панели инструментов по кнопке Run to cursor (Выполнять до курсора). Программа начнёт выполняться и снова остановится в том же месте (Рис. 6.138). Посмотрим значение счётчика времени — 500 мкс. Кроме того, из- менился логический уровень на выводе 1 Порта В. При каждом последующем щелчке по кнопке Run to cursor (Выполнять до курсора) на панели инструментов можно наблюдать, как всякий раз будет ме- няться логический уровень на выводе 1 Порта В, а счётчик времени будет прини- мать значения 1000 мкс, 1500 мкс, 2000 мкс и т. д. Отладка закончена. Если в процессе отладки требуется внести изменения в текст программы, то, не закрывая окно AVR Studio 4, следует перейти в IDE CodeVisionAVR, внести из- менения в программу, перестроить проект и вернуться в окно AVR Studio 4. При этом появится диалоговое окно AVR Studio с надписью: «Objectfile has changed, reload?» (Объектный файл был изменён, перезагрузить?). Для перезагрузки объектного файла следует щёлкнуть по кнопке Да, для отме- ны — по кнопке Нет. Отладка этого примера в Visual Micro Lab и Proteus VSM (см. Отладка про- граммы) даёт те же результаты (Рис. 6.139, 6.140).
6.3. Пример использования CodeWizardAVR 449 Рис. 6.134. Результат выполнения оператора TCNT0 = OxFE. Рис. 6.135. Результат выполнения оператора PORTB. 1 = ~PORTB. 1.
450 Глава 6. Автоматический генератор программ Code WizardA VR Рис. 6.136. Возврат в цикл while. Рис. 6.137. Обнуление счётчика времени.
6.3. Пример использования Code WizardA VR 451 Рис. 6.138. Измерение времени между сменами логического уровня на выводе 1 Порта В. Рис. 6.139. Результаты отладки в Visual Micro Lab.
452 Глава 6. Автоматический генератор программ CodeWizardAVR ss CodeWizard ISIS Professional (Animating) File View Ed* Library Tools Design Graph Source Debug Template System Help «« •. ЧйГ X X я s м a- a '«i й в i и Puc. 6.140. Результаты отладки в Proteus VSM.
ГЛАВА ПРИМЕРЫ ПРОЕКТОВ В этой главе будут рассмотрены примеры проектов, разработанных в CodeVisionAVR. Эти проекты находятся в поддиректории ..\EXAMPLES в соот- ветствующих папках. Для открытия проекта следует воспользоваться соответствующей командой (см. Команда File -» Open (Файл -> Открыть)), перейти в поддиректорию ..\EXAMPLES, выбрать папку с соответствующим проектом и в этой папке вы- брать файл с расширением .prj. Чтобы увидеть результаты работы той или иной программы, следует восполь- зоваться указанными в проекте отладочными платами либо самостоятельно соб- рать схему, приведённую к каждому проекту, и записать программу проекта в со- ответствующий микроконтроллер AVR (см. Запись программы в чип AVR). 7.1. Проект «Led» Этот проект демонстрирует использование таймера/счётчика 0 для задания временных промежутков определённой длительности. В этом проекте содержится один исходный файл — led.c. Текст этого файла с русифицированными комментариями: 1: */ 2 : Пример перемещающегося светодиода 3: 4: CodeVisionAVR С Compiler 5: (С) 2000-2002 HP InfoTech S.R.L. б: www.hpinfotech.ro 7: 8: Чип: AT90S8515 9: Модель памяти: SMALL 10: Размер стека данных: 128 Б 11: 12: 8 светодиодов подключены между выходами 13: PORTC и +5V через токоограничительные 14: резисторы 1К 15: Аноды светодиодов подключены к +5V
454 Глава 7. Примеры проектов 16: 17: На STK500 нужно только соединить 18: Разъёмы PORTC и LEDS между собой 19: с помощью 10-жильного кабеля 20: /* 21: 22: // определения регистров ввода/вывода для AT90S8515 23: #include <90s8515.h> 24: 25: // частота кварца [Гц] 26: tfdefine xtal 3686400 27: // частота перемещения светодиода [Гц] 28: tfdefine fmove 2 29: 30: // Светодиод будет включён при 0 на выходе ПОРТА С 31: unsigned char led_status=0xfe; 32: 33: // Программа обслуживания прерывания по переполнению 34: // таймера/счётчика 1, которое происходит каждые 0.5 секунды 35: 36: interrupt [TIM1_OVF] void timerl_overflow(void) 37: { 38: // снова инициализируем таймер/счётчик 1 39 : TCNT1=0x10000-(xtal/1024/fmove); 40: •// переместим светодиод 41: led_status<<=l; 42: led_statusI=1; 43: if (led_status==0xff) led_status=0xfe; 44: // включим светодиод 45: PORTC=led_status; 46: } 47: 48: void main(void) 49: { 50: // установим порты ввода/вывода 51: //все выводы PORTC - выходы 52: DDRC=0xff; 53: // включим первый светодиод 54: PORTC=led_status; 55: 56: // инициализируем таймер/счётчик 1 57: // таймер/счётчик 1 отсоединим от вывода ОС1 58: // ШИМ нет 59: TCCR1A=O; 60: // тактовая частота таймера/счётчика 1 - xtal/1024 61: TCCR1B=5; 62: // инициализируем таймер/счётчик 1 63 : TCNTl=0xl0000-(xtal/1024/fmove); 64: // очистим флаги прерываний таймера/счётчика 1 65: TIFR=0;
7.7. Проект «Led» 455 66: // разрешим прерывание по переполнению таймера/счётчика 1 67: TIMSK=0x80; 68: // запретим все другие источники прерываний 69: GIMSK=0; 70: 71: // глобальное разрешение прерываний 72: #asm 73: sei 74: #endasm 75: 76: // остальное делается посредством прерываний 77: //по переполнению таймера/счётчика 1 78: while (1); 79: } В этом файле знаками /* (начало) и */ (конец) выделен блок с комментария- ми, а знаком // — строки с комментариями (см. Комментарии). В блоке с комментариями (строки 1 ...20) даются рекомендации по настройке платы STK500 при аппаратной реализации данного проекта. Директивой #include (строка 23) подключается заголовочный файл для ис- пользуемого микроконтроллера AVR (AT90S8515). Перед компиляцией препро- цессор компилятора вставит вместо этой строки текст файла 90s8515.h (см. Ди- ректива ^include). Директивами #define (строки 26 и 28) определяются идентификаторы xtal и fmove. После этих двух строк перед компиляцией препроцессор компилятора за- менит в тексте программы xtal на 3686400 и fmove на 2. В случае модификации программы для других значений частоты кварца и частоты перемещения светоди- ода достаточно будет лишь заменить значения в указанных директивах #define, не изменяя остального текста программы (см. Директива ^define, #undef). Директи- ва #define очень удобна, но вовсе не обязательна. В строке 31 определяется и инициализируется переменная led_status типа unsigned char (беззнаковая, размером в 1 Б) (см. Типы данных). Начальное значе- ние этой переменной — Oxfe, или 0Ы1111110, те. в самом младшем бите — 0, а в остальных битах — 1 (правила записи чисел в различных системах счисления см. Константы). Строки 36...46 — это определение программы (функции) обслуживания пре- рывания по переполнению таймера/счётчика 1. В строке 36 осуществляется доступ к системе прерываний микроконтроллера AVR, на что указывает ключевое слово interrupt. Далее за этим ключевым словом в квадратных скобках должен идти номер вектора прерывания. Но здесь вместо но- мера стоит идентификатор TIM1_OVF. Дело в том, что этот идентификатор опре- делён директивой #define в заголовочном файле 90s8515.h. Перед компиляцией препроцессор компилятора заменит TIM1_OVF на цифру 7 (номер вектора пре- рывания), как определено в файле 90s8515.h. Вместо TIM1_OVF в квадратных скобках можно было написать 7, но TIM1_OVF нагляднее. Далее идёт ключевое слово void, указывающее на то, что функция не возвращает никаких значений, затем имя функции timerl_overflow (может быть произвольным, но лучше со
456 Глава 7. Примеры проектов смысловой нагрузкой). По этому имени далее в программе осуществляется вызов данной функции. Ключевое слово void в скобках указывает на то, что в эту функ- цию не передаются никакие параметры. Подробнее см. Использование прерыва- ний. В строке 37 открывающая фигурная скобка указывает начало тела программы обслуживания прерывания timer1-Overflow. В теле программы, в строке 39, вычисляется начальное значение тайме- ра/счётчика 1 по приведённой формуле, где: 0x10000 — значение, при котором происходит переполнение таймера/счётчика 1 (таймер/счётчик 1 — 16-битный, следовательно, значение, при котором произойдёт его переполнение — 216 = 0x10000); xtal — значение тактовой частоты чипа (частоты кварца); 1024 — коэффициент деления предварительного делителя частоты таймера/счётчика 1, т. е. таймер будет тактироваться частотой xtal/1024; (move — частота, с которой должно происходить прерывание программы и переключаться светодиоды. Полу- ченное начальное значение записывается в таймер/счётчик 1 (в регистр TCNT1). Это необходимо, т. к. после переполнения таймера/счётчика 1 его значение обну- ляется. В строке 41 значение переменной led_status сдвигается на один бит влево, и полученное значение присваивается переменной led_status (знак операции — <<= 1). Например, при начальном значении led_status = 0b 1111 1110, после вы- полнения строки 41 значение этой переменной станет led_status = 0b 1111 1100, т. к. при сдвиге влево на 1 бит младший бит становится равным 0, а самый стар- ший теряется (см. Бинарные операции). В строке 42 с переменной led_status осуществляется операция побитного ИЛИ с присваиванием (знак операции — |=) с числом 1 = 0Ь 0000 0001. При этом в переменной led_status младший бит независимо от его значения станет равным 1, а значения остальных битов не изменятся. Например, при значении ledjstatus = 0b 1111 1100 после выполнения строки 42 значение этой переменной станет led_status = 0b 1111 1101 (см. Бинарные операции). Таким образом, при каждом выполнении строк 41 и 42 бит со значением 0 в переменной led_status будет перемещаться на одну позицию влево. В строке 43 находится укороченный оператор if-else (без else) (см. Оператор if-else). Так как в его теле всего один оператор, вся конструкция за- писана в одну строку, без фигурных скобок. В условии оператора if-else (в скоб- ках) проверяется равенство значения переменной ledjstatus числу Oxff=Ob 1111 1111 (знак операции — ==, см. Бинарные операции). Если равенство выполняется (истина), то переменной ledjstatus присваивается значение Oxfe = 0b 1111 1110 и выполнение программы продолжается со строки 45. Если равенство НЕ выпол- няется (ложь), т. е. значение переменной ledjstatus не равно Oxff = 0b 1111 1111, то эта строка программы пропускается, а выполнение программы продолжается со строки 45. Таким образом, после того как бит со значением 0 будет сдвинут влево из са- мого старшего бита (значение переменной ledjstatus станет равно Oxff = 0b 1111 ИИ), этой переменной будет опять присвоено первоначальное значение (как в строке 31).
7.1. Проект «Led» 457 В строке 45 текущее значение переменной led_status записывается в регистр PORTx Порта С (см. Доступ к регистрам ввода/вывода). При этом будет включён светодиод, подключённый к тому биту порта, значение которого равно 0. В строке 46 закрывающая фигурная скобка указывает конец тела программы обслуживания прерывания. Строки 48...79 — это определение основной функции программы (main). Эта функция обязательно должна присутствовать во всех программах (но только ОД- НА!). Именно с этой функции, в каком бы месте программы она ни находилась, начинается выполнение программы. В строке 48 осуществляется объявление функции main. Вначале стоит ключе- вое слово void, указывающее на то, что эта функция не возвращает никаких зна- чений, затем следует имя функции main (ЕГО ИЗМЕНЯТЬ НЕЛЬЗЯ!) и ключевое слово void в скобках, которое указывает на то, что в эту функцию не передаются никакие параметры (см. Функции). В строке 49 открывающая фигурная скобка указывает начало тела функции main. В строке 52 в регистр DDRx Порта С во все биты записываются 1 (Oxff = 0b 1111 1111), тем самым все выводы этого порта делаются выходами. В строке 54 в регистр PORTx Порта С записывается значение переменной ledjstatus. Подробнее о регистрах DDRx и PORTx см. Доступ к регистрам ввода/вывода. В строках 59...69 записью определённых значений в соответствующие регист- ры осуществляется инициализация таймера/счётчика 1 (его конфигурация, зада- ние тактовой частоты и начального значения), а также настройка системы преры- ваний (очистка определённых флагов, разрешение прерывания по переполне- нию таймера/счётчика 1, запрещение всех других прерываний). Подробнее смотрите описание (datasheet) от Atmel на микроконтроллер AT90S8515 (см. Ко- манда Help -» AVR Data Sheets (Помощь -» Описания AVR)). В строках 72...74 осуществляется глобальное разрешение прерываний. Для этого в программу включён ассемблерный код. В строке 72 директива #asm гово- рит компилятору о начале ассемблерного кода, а в строке 74 директива #endasm — о его завершении (см. Директивы #asm и ttendasm). В строке 73 инструкция ассем- блера sei устанавливает флаг глобального прерывания I в регистре статуса SREG микроконтроллера, тем самым разрешая глобальные прерывания. Подробнее об инструкциях ассемблера см. систему помощи бесплатной программы AVR Studio от Atmel, которую можно скачать на сайте разработчиков микроконтроллеров AVR http://www.atmel.com. В строке 78 реализован бесконечный цикл с помощью оператора while (см. Оператор while). Этот оператор будет выполняться до тех пор, пока выражение в скобках истинно, т. е. не 0. Выражение в скобках — 1 (можно также написать 5 или 89), а не 0, т. е. всегда истинно. Следовательно, этот оператор будет выпол- няться бесконечно, пока не произойдёт прерывание программы. В строке 79 закрывающая фигурная скобка указывает конец тела функции main. Программа работает следующим образом. После подачи питания (или аппа- ратного сброса на выводе RESET микроконтроллера) начинает выполняться функция main. При этом происходит конфигурирование периферийных уст- ройств и настройка системы прерываний микроконтроллера. Запускается тай- мер/счётчик 1. Все выводы Порта С настраиваются как выходы, в самый млад-
458 Глава 7 Примеры проектов ший бит записывается 0, а в остальные — 1. После этого программа переходит к выполнению бесконечного цикла while и выполняет его, пока не произойдёт пре- рывание по переполнению таймера/счётчика 1. При переполнении таймера/счётчика 1 происходит прерывание, и программа переходит к выполнению функции обработки прерывания timerl_overflow. В тай- мер/счётчик 1 снова записывается начальное значение, а значение 0 на одном из выводов Порта С перемещается на один бит влево (в более старший бит). После этого программа возвращается в бесконечный цикл while и выполняет его, пока снова не произойдёт переполнение таймера/счётчика 1, и т. д. Если значение О находилось в 7-м, самом старшем бите Порта С, то при выполнении функции об- работки прерывания значение 0 перейдёт в 0-й, самый младший бит Порта С, и всё повторится сначала. При моделировании данного проекта в AVR Studio версии 4 (пример модели- рования см. в главе Пример использования Code WizardAVR) значение времени меж- ду переключениями светодиодов будет поочередно получаться то 500000.27 мкс (Рис. 7.1), то 499999.73 мкс (Рис. 7.2). Рис. 7.1. Первое измерение времени между переключением светодиодов на выводах Порта С. Рис. 7.2. Второе измерение времени между переключением светодиодов на выводах Порта С.
7.1. Проект «Led» 459 Длительность одного машинного цикла при тактовой частоте 3.6864 МГц: t = 1/3.6864 = 0.2712673611111 мкс » 0.27 мкс, т. е. погрешность задания времени переключения составляет ± один машинный цикл. Эта погрешность объясняется тем, что для выполнения всех операторов программы между двумя переключени- ями светодиодов требуется нечётное количество машинных циклов. Для устране- ния этой погрешности можно воспользоваться ассемблерным приёмом — доба- вить ассемблерную инструкцию пор, которая ничего не делает, но на её выполне- ние тратится один машинный цикл: 35: 36: interrupt [TIM1_OVF] void timerl_overflow(void) 37: { 38: // снова инициализируем таймер/счётчик 1 39: TCNTl=0xl0000-(xtal/1024/fmove); 40: // переместим светодиод 41: led_status<<=l; 42: led_status1=1; 43: if (led_status==0xff) led_status=Oxfe; 44: // включим светодиод 45: PORTC=led_status; 46: 47: // добавление ассемблерной инструкции nop 48: 49: #asm nop 50: ttendasm 51: } 52: Теперь значение времени между переключением светодиодов составит ровно 500000 мкс (0.5 с) (Рис. 7.3), хотя, конечно, в данном проекте такая точность не требуется. Рис. 7.3. Повышение точности установки времени между переключением светодиодов на выводах Порта С.
460 Глава 7. Примеры проектов Результат выполнения этой программы представлен на Рис. 7.4. DD1 AT90S8515 PB5/MOSI PB6/MISO PB7/SCK XTAL1 XTAU RESET РВО/ТО РВ1/Т1 P82/A1N0 PB3/AIN1 POO/RXD РО1/ТХО PD2/INT0 PO3/INT1 PD4 PD5/OC1A PD6/WR PD7/RO РАО/АОО РА1/АО1 PA2/AD2 РАЗ/АОЗ PA4/AD4 РА5 AD5 PA6/AD6 РА7/АО7 РСО/Ав РС1/АЭ РС2/А10 РСЗ/А11 РС4/А12 РС5/А13 РС6/А14 РС7/А15 21 22 25 26 27 ОС1В XTAL1 XTAL2 RESET РВО/ТО Р81/Т1 PB2/AIN0 PB3/AIN1 PB4/SS PB5/MOSI PB6/MISO PB7/SCK PDO/RXO PO1/TXD PD2/INT0 PO3/INT1 PD4 РО5/ОС1А PD6/WR PO7/RO DD1 AT90S8515 PAO/ADO PA1/AD1 PA2/AD2 РАЗ/АОЗ PA4/AD4 PA5/AD5 PA6/AD6 РА7/АО7 ОС1В ЮР РСО/Ав РС1/А9 РС2/А10 РСЗ/А11 РС4/А12 РС5/А13 РС6/А14 РС7/А15 t = t0 + 0.5 С Рис. 7.4. Результат работы программы — перемещение светящегося светодиода. Кстати, в этой программе вообще можно обойтись без переменной led_status. 1: /* 2 : Пример перемещающегося светодиода 3: 4: Программа без переменной led_status 5: б: Чип: AT90S8515 7: Модель памяти: SMALL 8: Размер стека данных: 128 Б 9: 10: */ 11: 12: // определения регистров ввода/вывода для AT90S8515 13: ttinclude <90s8515.h> 14: 15: // частота кварца [Гц] 16: tfdefine xtal 3686400 17: // частота перемещения светодиода [Гц] 18: tfdefine fmove 2 19: 20: // Программа обслуживания прерывания по переполнению таймера/счётчика!, 21: // которое происходит каждые 0.5 секунды 22: 23: interrupt [TIM1_OVF] void timerl_overflow(void) 24: { 25: // снова инициализируем таймер/счётчик 1 26 : TCNT1 = 0x10000-(xtal/1024/fmove);
7.1. Проект «Led» 461 27: // переместим и включим светодиод 28: PORTC=(PORTC«1) 11; 29: if (PORTC==0xff) PORTC=Oxfe; 30: } 31: 32: void main(void) 33: { 34: // установим порты ввода/вывода 35: // все выводы PORTC - выходы 36: DDRC=0xff; 37: // включим первый светодиод 38: PORTOOxfe; 39: 40: // инициализируем таймер/счётчик 1 41: // таймер/счётчик 1 отсоединим от вывода 0С1 42: // ШИМ нет 43: TCCR1A=O; 44: // тактовая частота таймера/счётчика 1 - xtal/1024 45: 46: TCCR1B=5; // инициализируем таймер/счётчик 1 47: TCNTl=0xl0000-(xtal/1024/fmove); 48: // очистим флаги прерываний таймера/счётчика 1 49: TIFR=0; 50: // разрешим прерывание по переполнению таймера/счётчика 1 51 : TIMSK=0x80; 52: // запретим все другие источники прерываний 53: GIMSK=0; 54: 55: // глобальное разрешение прерываний 56: #asm 57: sei 58: ttendasm 59: 60: // остальное делается посредством прерываний 61: //по переполнению таймера/счётчика 1 62: while (1) ; 63: 64: } При этом размер скомпилированной программы сократится со 132 слов до 115, уменьшится размер используемого SRAM, т. к. не требуется отводить место под переменную led_status. Кроме того, т. к. число машинных циклов между дву- мя переключениями светодиодов получается чётным, отпадает необходимость в дополнительной инструкции пор — время получается точно 0.5 с. При отсутствии отладочной платы STK500 можно самостоятельно реализо- вать этот проект по приведённой схеме.
462 Глава 7. Примеры проектов 7.2. Проект «ADC8535» Этот проект демонстрирует использование внутреннего аналого-цифрового преобразователя (АЦП) микроконтроллера AT90S8535. В этом проекте содержится один исходный файл — adc8535.c. Текст этого файла с русифицированными комментариями: 1: /* 2: Пример АЦП для AT90S8535 на 3: плате Atmel STK500 4: 5: CodeVisionAVR С Compiler б: © Copyright 2001-2002 HP InfoTech S.R.L. 7: www.hpinfotech.ro 8: 9: Установите следующие перемычки на плате STK500: 10: VTARGET, AREF, RESET, XTAL1, OSCSEL: 1-2 11: 12: Соедините разъёмы PORTB и LEDS с помощью 13: 10-жильного плоского кабеля 14: 15: Соедините разъёмы ISP6PIN и SPROG3 с помощью 16: б-жильного плоского кабеля 17: 18: AT90S8535 должен быть расположен в панельке SCKT3100A3 19: 20: В AVR Studio в окне Tools ISTK500I Board установить: 21: - VTarget=5V 22: - ARef=5V 23: - 0scillator=3.69МГц 24: 25: Подайте положительное DC напряжение в дипазоне 0...5V 26: между выводами РАО и GND разъёма PORTA 27: */ 28: 29: // определения регистров ввода/вывода для AT90S8535 30: ^include <90s8535.h> 31: // функции задержки 32: tfinclude <delay.h> 33: 34: tfdefine ADC_VREF_TYPE 0x00 35: 36: // Программа обслуживания прерывания АЦП 37: interrupt [ADC_INT] void adc_isr(void) 38: { 39: // Светодиоды отображают 8 наиболее 40: // значимых битов АПП 41: PORTB=(unsigned char) ~(ADCW>>2); 42: // задержка 20 мс 43: delay_ms(20);
7.2. Проект «ADC8535» 463 44: /7 Начало нового АЦ преобразования 45: ADCSR1=0x40; 46: 47: 48: void main(void) 49: { 50: // Инициализация Порта В 51: PORTB=0xFF; // все выходы 52: DDRB=0xFF; // все светодиоды первоначально выключены 53: 54: // Инициализация АЦП 55: // Тактовая частота АЦП: 57.656 кГц 56: // Прерывания АЦП: Вкл 57: ADCSR=0x8E; 58: 59: // Глобальное разрешение прерываний 60: #asm("sei") 61: 62: // Выберем вход 0 АЦП 63: ADMUX=0; 64: 65: // Запустим первое АЦ преобразование 66: ADCSRI=0x40; 67: 68: // Вся работа делается посредством АЦП прерываний 69: while (1); 70: } В этом файле знаками /* (начало) и */ (конец) выделен блок с комментария- ми, а знаком // — строки с комментариями (см. Комментарии). В блоке с комментариями (строки 1 ...27) даются рекомендации по настройке платы STK500 при аппаратной реализации данного проекта. Директивами #include (строки 30 и 32) подключаются: заголовочный файл для используемого микроконтроллера AVR (AT90S8535) — файл 90s8535.h; и функ- ции задержки — файл delay.h (см. Функции задержки). Перед компиляцией пре- процессор компилятора вставит вместо этих строк текст соответствующих фай- лов {см. Директива ^include). Директивой #define (строка 34) определяется идентификатор ADC_VREF_TYPE. Перед компиляцией препроцессор заменит в тексте програм- мы этот идентификатор на его значение — 0x00. Так как далее в программе ADC_VREF_TYPE нигде не используется, вероятнее всего, что эта строка осталась от предыдущих модификаций программы, и её можно смело закомментировать (поставить перед ней знак //): 33: 34: // tfdefine ADC_VREF_TYPE 0x00 35:
464 Глава 7. Примеры проектов Строки 37...46 — это определение программы (функции) обслуживания пре- рывания от АЦП (ADC). Это прерывание происходит при окончании каждого аналого-цифрового преобразования. В строке 37 осуществляется доступ к системе прерываний микроконтролле- ра AVR, на что указывает ключевое слово interrupt. Далее за этим ключевым сло- вом в квадратных скобках должен идти номер вектора прерывания. Но здесь вместо номера стоит идентификатор ADCJNT Дело в том, что этот идентифи- катор определён директивой #define в заголовочном файле 90s8535.h. Перед ком- пиляцией препроцессор компилятора заменит ADC_INT на цифру 15 (номер вектора прерывания), как определено в файле 90s8535.h. Вместо ADC_INT в квадратных скобках можно было написать 15, но ADC_INT нагляднее. Далее идёт ключевое слово void, указывающее на то, что функция не возвращает ника- ких значений, затем имя функции adc_isr (может быть произвольным, но лучше со смысловой нагрузкой). По этому имени далее в программе осуществляется вызов данной функции. Ключевое слово void в скобках указывает на то, что в эту функцию не передаются никакие параметры. Подробнее см. Использование прерываний. В строке 38 открывающая фигурная скобка указывает начало тела программы обслуживания прерывания. В теле программы обслуживания прерывания от АЦП в строке 41 в регистр PORTx Порта В записывается значение выражения, стоящего справа от знака «=». Рассмотрим это выражение подробнее. ADCW — это переменная, которая определена в файле 90s8535.h. Она имеет размер 16 бит (2 байта), и для доступа к ней в файле 90s8535.h используется ключевое слово sfrw (см. Доступ к регист- рам ввода/вывода). В переменной ADCW CodeVisionAVR сохраняет 10-битный результат аналого-цифрового преобразования АЦП (в битах 0...9). В 0-м бите хранится самый младший бит результата преобразования, а в 9-м — самый стар- ший. Так как Порт В, к которому подключены светодиоды, имеет всего 8 битов, то в данном проекте светодиодами будут отображаться только 8 наиболее значимых (самых старших) битов результата аналого-цифрового преобразования АЦП. Для этого значение переменной ADCW сдвигается на два бита вправо (знак операции — »2, см. Бинарные операции). При этом значения 1-го и 0-го битов пропадают. Теперь 8 наиболее значимых битов результата аналого-цифрового преобразова- ния будут содержаться в младшем байте (биты 0...7) переменной ADCW. Так как светодиоды, подключённые к выводам Порта В, загорятся только тог- да, когда на соответствующем выводе будет логический «О», то требуется заме- нить в переменной ADCW все 1 на 0, а 0 — на 1. Это делается с помощью операции побитного инвертирования (знак операции — ~, см. Унарные операции). Так как PORTB — это байт (8 бит), a ADCW — это слово (2 байта = 16 бит), то, прежде чем выполнить операцию присваивания (знак операции — =, см. Бинарные операции), нужно преобразовать тип ADCW в беззнаковый байт (unsigned char). При таком преобразовании типов данных у переменной ADCW останется только младший байт (старший будет отброшен) (см. Преобразования типов).
7.2. Проект «ADC8535» 465 Полученный результат помещается (присваивается операцией «=») в регистр PORTx Порта В. Если при этом в регистре DDRx Порта В все биты установлены в 1, т. е. все выводы Порта В являются выходами (см. Доступ к регистрам ввода/вы- вода}, то светодиоды, подключённые к выводам Порта В, отобразят старшие 8 битов результата преобразования АЦП, причём включенный светодиод будет обозначать 1 в соответствующем бите, а выключенный — 0. Далее, в строке 43, с помощью функции delay ms осуществляется задержка в 20 мс (см. Функции задержки}, чтобы в реальном устройстве можно было увидеть переключение светодиодов. В строке 45 осуществляется запуск нового аналого-цифрового преобразова- ния АЦП. Для этого в регистре ADCSR (ADC Control and Status Register — Регистр управления и состояния АЦП) микроконтроллера необходимо установить в еди- ницу бит 6 — ADSC (ADC Start Conversion — Старт преобразования АЦП). Этот бит имеет значение 1 во время всего цикла преобразования и сбрасывается в 0 по его окончании. Подробнее смотрите описание (datasheet) от Atmel на микроконт- роллер AT90S8535 (см. Команда Help AVR Data Sheets (Помощь Описания AVR)). Для установки бита ADSC в 1 используется операция побитового ИЛИ с присваиванием (знак операции — |=, см. Бинарные операции) с числом 0x40 = 0Ь 0100 0000. В результате этой операции в 6-й бит в регистре ADCSR (бит ADSC) будет записана 1, независимо от того, какое значение там было до этого. Значе- ния остальных битов регистра ADCSR не изменятся. В строке 46 закрывающая фигурная скобка указывает конец тела программы обслуживания прерывания. Строки 48...70 — это определение основной функции программы (main). Эта функция обязательно должна присутствовать во всех программах (но только ОДНА!). Именно с этой функции, в каком бы месте программы она ни находи- лась, начинается выполнение программы. В строке 48 осуществляется объявление функции main. В начале строки стоит ключевое слово void, указывающее на то, что эта функция не возвращает никаких значений, затем следует имя функции main (ЕГО ИЗМЕНЯТЬ НЕЛЬЗЯ!) и клю- чевое слово void в скобках, которое указывает на то, что в эту функцию не переда- ются никакие параметры (см. Функции). В строке 49 открывающая фигурная скобка указывает на начало тела функции main. В строках 51 и 52 в регистры DDRx и PORTx Порта В во все биты записывает- ся 1 (Oxff=Ob 1111 1111), тем самым все выводы этого порта делаются выходами, и на всех выводах устанавливается значение логической 1. Таким образом, все светодиоды, подключённые к выводам Порта В, выключаются. Подробнее о ре- гистрах DDRx и PORTx см. Доступ к регистрам ввода/вывода. В строке 57 производится инициализация АЦП. При этом в регистр ADCSR (ADC Control and Status Register — Регистр управления и состояния АЦП) запи- сывается значение 0х8Е = 0Ь 1000 1110. Значение 1 в 7-м бите разрешает АЦП; 0 в 6-м бите запрещает аналого-цифровое преобразование; 0 в 5-м бите запрещает режим непрерывного преобразования; 0 в 4-м бите — флаг прерывания от АЦП сброшен; 1 в 3-м бите разрешает прерывание от АЦП (если установлено глобаль- ное разрешение прерываний). Код 110 в трёх младших битах (0...2) задаёт коэф-
466 Глава 7. Примеры проектов фициент деления тактовой частоты АЦП, равный 64, т. е. АЦП будет тактиро- ваться частотой: 3690 / 64 = 57.65625 кГц « 57.656 кГц. Подробнее см. описание (datasheet) от Atmel на микроконтроллер AT90S8535 (см. Команда Help -+AVR Data Sheets (Помощь -> Описания AVR)), В строке 60 осуществляется глобальное разрешение прерываний. Для этого в программу включён ассемблерный код (см. Директивы #asm и ttendasm). Инс- трукция ассемблера sei устанавливает флаг глобального прерывания I в регистре статуса SREG микроконтроллера, тем самым разрешая глобальные прерывания. Подробнее об инструкциях ассемблера см. систему помощи бесплатной програм- мы AVR Studio от Atmel, которую можно скачать на сайте разработчиков микро- контроллеров AVR http://www.atmel.com. В строке 63 выбирается один из 8 каналов АЦП. Для этого в регистр ADMUX (ADC Multiplexer Select Register — Регистр выбора мультиплексора АЦП) запи- сывается соответствующий двоичный код номера канала: для 7-го — 111, для 6-го -110,..., для 0-го — ООО. В данном проекте выбран 0-й канал (в регистр ADMUX записывается код ООО). Подробнее см. описание (datasheet) от Atmel на микро- контроллер AT90S8535 (см. Команда Help AVR Data Sheets (Помощь Описания AVR)). В строке 66 осуществляется запуск первого преобразования АЦП, как было рассмотрено при описании строки 45 программы, т. е. бит 6 (ADSC) регистра ADCSR микроконтроллера устанавливается в 1. В строке 69 реализован бесконечный цикл с помощью оператора while (см. Оператор while). Этот оператор будет выполняться до тех пор, пока выраже- ние в скобках истинно, т. е. не 0. Выражение в скобках — 1 (можно также напи- сать 5 или 89), а не 0, т. е. всегда истинно. Следовательно, этот оператор будет вы- полняться бесконечно, пока не произойдёт прерывание программы. В строке 70 закрывающая фигурная скобка указывает конец тела функции main. Программа работает следующим образом. После подачи питания (или аппа- ратного сброса на выводе RESET микроконтроллера) начинает выполняться функция main. При этом происходит конфигурирование периферийных уст- ройств (Порта В и АЦП) и настройка системы прерываний микроконтроллера. Запускается первое преобразование АЦП. После этого программа переходит к выполнению бесконечного цикла while и выполняет его, пока не произойдёт пре- рывание по окончании преобразования АЦП. После этого программа переходит к выполнению функции обработки преры- вания adc_isr. На выводах Порта В устанавливаются инвертированные значения старших 8 битов результата преобразования АЦП. При этом загораются соответс- твующие светодиоды. Делается пауза в 20 мс, и снова запускается преобразование АЦП. После этого программа возвращается в бесконечный цикл while и выполня- ет его, пока снова не произойдёт прерывание по окончании преобразования АЦП, и т. д.
7.2. Проект «ADC8535» 467 Результаты выполнения этой программы представлены на Рис. 7.5, Рис. 7.6. Цепь сброса, питания и кварцевый резонатор микроконтроллера не показаны (см. Рис. 4.3). При входном напряжении АЦП 5 В результат преобразования будет ОЬ 1111 111111, т. к. опорное напряжение также равно 5 В. Один младший бит результата преобразования соответствует напряжению: 5 В / 1024 = 0.0048828125 В « 0.00488 В. Таким образом, при входном напряжении 3.5 В результат преобразования бу- дет: 3.5 В / 0.00488 В « 717 = 0Ь 0010 1100 1101, т. е. 8 старших битов — 0Ь 1011 ООП, и будут гореть светодиоды VD8 (самый старший бит), VD6, VD5, VD2 и VD1 (Рис. 7.5). VD1 R3 470 VCC VD2 VD5 VD6 VD7 R7 470 VD8 R8 470 PD0/RXD PO1/TXD PD2/INT0 PD3/INT1 PD4/OC1B PD5/OC1A PD6/ICP PD7/OC2 XTAL1 XTAL2 RESET РВО/ТО РВ1/Т1 PB2/AIN0 PB3/AIN1 PB4/SS PB5/MOSI PB6/MISO PB7/SCK DD1 AT90S8535 R4 470 VD3 VD4 VCC VCC AREF AGNO AVCC PA0/ADC0 PA1/ADC1 PA2/ADC2 PA3/ADC3 РА4/А0С4 PA5/ADC5 PA6/ADC6 PA7/ADC7 RV1 1 кОм PC0 РС1 РС2 РСЗ РС4 РС5 PC6/TOSC1 PC7/TOSC2 40 GND 10 мкГн С1 100 нФ GND GND R9 1 кОм Рис. 7.5. Результат работы программы при входном напряжении 3.5 В. При входном напряжении 1.5 В результат преобразования будет: 1.5 В/ 0,00488 В « 307 = 0Ь 0001 ООП ООП, т. е. 8 старших битов — 0Ь 0100 1100, и будут гореть светодиоды VD7, VD4 и VD3 (Рис. 7.6). При отсутствии отладочной платы STK500 можно самостоятельно реализо- вать этот проект по приведённой схеме.
468 Глава 7. Примеры проектов VD1 VD2 R2 470 VD3 VD4 VD7 VD8 R8 470 VD5 R5 470 VD6 R6 470 A. 19 DD1 AT90S8535 XTAL1 XTAL2 RESET PBO/TO PB1/T1 PB2/AIN0 PB3/AIN1 PB4/SS PB5/MOSI PB6/MISO PB7/SCK PDO/RXD PD1/TXD PD2/INT0 PD3/INT1 PD4/OC1B PD5/OC1A PD6/ICP PD7/OC2 AREF AGND AVCC PAO/ADCO PA1/ADC1 PA2/ADC2 PA3/ADC3 PA4/ADC4 PA5/ADC5 PA6/ADC6 PA7/ADC7 PCO PC1 PC2 РСЗ PC4 PC5 PC6/T0SC1 PC7/TOSC2 GND VCC GND C1 100 нФ RV1 1 кОм R9 1 кОм L1 10 мкГн 2 Рис. 7.6. Результат работы программы при входном напряжении 1.5 В. 7.3. Проект «C_asm» Этот проект демонстрирует вызов функции, написанной на ассемблере, из программы Си. В этом проекте содержится один исходный файл — c_asm.c. Текст этого фай- ла с русифицированными комментариями: 1: 2: 3: Пример показывает, как из Си вызывать функцию, написанную на ассемблере 4: 5: б: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: CodeVisionAVR С Compiler (С) 2000-2002 HP InfoTech S.R.L. www.hpinfotech.ro // функция объявляется в ассемблере // эта функция возвращает а+Ь+с #pragma warn- // это запретит предупреждения int sum_abc(int { #asm Idd r30,y+3 Idd r31,y+4 a, int b, unsigned char с) ;R30=LSB a ;R31=MSB a
7.3. Проект «C asm» 469 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43 : 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: Idd г26,у+1 ;R26=LSB b Idd г27,у+2 ;R27=MSB b add r30,r26 adc r31,r27 ;(R31,R30)=a+b Id г26,у ;R26=c clr г27 /преобразуем unsigned char с в int add r30,r26 adc r31,r27 tfendasm } ;(R31,R30)=(R31,R30)+C tfpragma warn+ // разрешим предупреждения void main(void) { int r; // теперь вызовем функцию и сохраним результат в г r=sum_abc(2,4/6); } /* Некоторые пояснения: Компилятор передаёт функциональные параметры, используя Data Stack (Стек данных). Сначала он помещает целый (integer) параметр а, затем b и, наконец, символьный без знака (unsigned char) параметр с. При каждом помещении параметра в Стек данных регистра Y декрементируется (уменьшается) в зависимости от размера параметра (на 4 для long int (длинное целое), на 2 для int (целое), на 1 для char (символ)). Для многобайтовых параметров сначала помещается MSB (старший байт). Как видно, Стек данных растёт вниз. После того как все параметры функций будут помещены в Стек данных, регистр Y указывает на последний параметр с, так что мы можем прочитать его значение в R26, используя инструкцию: Id г26,у. Параметр b был помещён перед с, так что он находится по более старшему адресу в Стеке данных. Мы прочитаем его, используя инструкции: Idd r27,y+2 (MSB) и Idd r26,у+1 (LSB). MSB был помещён первым, так что он - по более старшему адресу. Параметр а был помещён перед Ь, так что он находится по ещё более старшему адресу в Стеке данных. Мы прочитаем его, используя инструкции: Idd r31,y+4 (MSB) и Idd r30,y+3 (LSB). Функции возвращают свои значения в регистры: R30 для char и unsigned char R30, R31 для int и unsigned int R30, R31, R22, R23 для long и unsigned long. Итак, наша функция должна возвращать свой результат в регистры R30, R31. После возврата из функции компилятор автоматически сгенерирует код,
470 Глава 7. Примеры проектов 69: чтобы восстановить пространство Стека данных, использованное 70: параметрами функции, так что вы не должны об этом беспокоиться. 71: Директива компилятора #pragma warn- запрещает компилятору 72: генерировать предупреждения о том, что наша функция не возвращает 73: значение. 74: Это необходимо, поскольку компилятор не знает того, что мы делаем в 75: нашей ассемблерной части функции. 76: */ В этом файле знаками /* (начало) и */ (конец) выделен блок с комментария- ми, а знаком // — строки с комментариями (см. Комментарий). В блоке с комментариями (строки 1...8) содержится информация о данном проекте. Директивой #pragma warn- (строка 12) компилятору запрещается выдавать предупреждения (см. Директива ^pragma warn). При отсутствии этой директивы компилятор Си будет выдавать предупреждения о том, что функция не возвраща- ет никаких значений, хотя объявлена без ключевого слова void, а также о том, что в теле функции не используются объявленные переменные а, b и с, т. к. компиля- тор Си не знает, что делается в ассемблерной части функции. Строки 14...28 — это определение функции sum_abc. В строке 14 осуществляется объявление функции. Определитель int (целое) задаёт тип значения, которое возвращает функция (см. Типы данных). Далее идёт имя функции sum_abc (может быть произвольным). По этому имени далее в про- грамме осуществляется вызов данной функции. В скобках указывается тип и ко- личество формальных параметров, которые передаются в функцию: два парамет- ра типа int (целое) и один типа unsigned char (беззнаковый байт) (см. Функции). В строке 15 открывающая фигурная скобка указывает начало тела функции sumabc. Тело функции состоит из ассемблерного кода. В строке 16 директива #asm го- ворит компилятору о начале ассемблерного кода, а в строке 27 директива #endasm — о его завершении (см. Директивы #asm и ttendasm). Сам ассемблерный код подробно описан в блоке комментариев (строки 40...74). Лишь отметим, что в строке 24 переменная с типа unsigned char (беззнаковый байт) преобразуется в пе- ременную типа int (целое). Для этого под неё отводится дополнительный байт (регистр R27). Обратите внимание, что комментарии в ассемблерной части кода отде- ляются с помощью точки с запятой (;). Иначе будет выдаваться ошибка при ассемблировании (при построении проекта). Подробнее об инструкциях ассемблера см. систему помощи бесплатной про- граммы AVR Studio от Atmel, которую можно скачать на сайте разработчиков мик- роконтроллеров AVR http://www.atmel.com. В строке 28 закрывающая фигурная скобка указывает конец тела функции sum_abc. В строке 30 директивой #pragma warn+ компилятору вновь разрешается выда- вать предупреждения (см. Директива ttpragma warn).
7.4. Проект «Multfile» 471 Строки 32...38 — это определение основной функции программы (main). Эта функция обязательно должна присутствовать во всех программах (но только од- на!). Именно с этой функции, в каком бы месте программы она ни находилась, начинается выполнение программы. В строке 32 осуществляется объявление функции main. В начале стоит ключе- вое слово void, указывающее на то, что эта функция не возвращает никаких зна- чений, затем следует имя функции main (его изменять нельзя!) и ключевое слово void в скобках, которое указывает на то, что в эту функцию не передаются ника- кие параметры (см. Функции). В строке 33 открывающая фигурная скобка указывает начало тела функции main. В строке 34 объявляется переменная г типа int (целое) (см. Типы данных). В строке 37 осуществляется вызов функции sum_abc с фактическими парамет- рами (числа 2, 4 и 6). Значение, которое возвращает функция, присваивается пе- ременной г (знак операции — =, см. Бинарные операции). Важно, чтобы тип пере- менной и тип значения, возвращаемого функцией, совпадали. В строке 38 закрывающая фигурная скобка указывает конец тела функции main. Для получения более подробной информации см. Включение в программу ас- семблерного кода. Программа работает следующим образом. После подачи питания (или аппа- ратного сброса на выводе RESET микроконтроллера) начинает выполняться функция main. При этом объявляется переменная г и осуществляется вызов функции sum_abc с фактическими параметрами. После этого программа перехо- дит к выполнению ассемблерного кода функции sum_abc. После его выполнения программа возвращается в функцию main, в место вызова функции sum_abc, и присваивает результат её выполнения переменной г. На этом выполнение про- граммы заканчивается. 7.4. Проект «Multfile» Этот пример демонстрирует взаимодействие нескольких файлов в многофай- ловом проекте. В этом проекте содержится несколько файлов: заголовочные — filel.h, filel.h, file3.h и исходные — filel.c, filel.c, ШеЗ.с и mainfile.c. Текст заголовочных файлов с русифицированными комментариями: filel.h: 1: // прототип объявления функции "fund", 2: // расположенной в файле проекта "filel.c" 3 : int fund (int a) ; filel.h: 1: // прототип объявления функции "func2", 2: // расположенной в файле проекта "file2.c" 3: int func2(int а);
472 Глава 7. Примеры проектов file3.h: 1: // прототип объявления функции "func3", 2: // расположенной в файле проекта "file3.c" 3: int func3(int a); Во всех этих файлах знаком // выделены строки с комментариями (строки 1, 2) (см. Комментарии). В строке 3 в этих файлах объявляются функции fund, func2 и func3. Определи- тель int (целое) задаёт тип значения, которое возвращает функция (см. Типы дан- ных). Далее идёт имя функции соответственно fiincl, func2 и func3 (может быть произвольным). По этому имени далее в программе осуществляется вызов дан- ной функции. В скобках указывается тип и количество формальных параметров, которые передаются в функцию: один параметр типа int (целое) (см. Функции). Текст исходных файлов с русифицированными комментариями: filel.c: 1: // эта функция объявлена в заголовочном файле "filel.h" 2: int fund (int a) { 3: return a+a; 4: } file2.c: 1: // эта функция объявлена в заголовочном файле "file2.h" 2 : int func2(int а) { 3: return а*а; 4: } file3.c: 1: // эта функция объявлена в заголовочном файле "file3.h" 2: int func3(int а) { 3: return а*3; 4: } Во всех этих файлах знаком // выделена строка с комментариями (строка 1) (см. Комментарии). Строки 2...4 — это определение функций fund, func2 и func3 соответственно. В строке 2 осуществляется объявление функции. Определитель int (целое) за- даёт тип значения, которое возвращает функция (см. Типы данных). Далее идёт имя функции, соответственно fund, func2 и func3 (может быть произвольным, но должно совпадать с именем в соответствующих файлах .h). По этому имени далее в программе осуществляется вызов данной функции. В скобках указывается тип и количество формальных параметров, которые передаются в функцию: один пара- метр типа int (целое). Открывающая фигурная скобка указывает начало тела фун- кций fund, fiinc2 и func3 (см. Функции). Тело каждой функции состоит из одного оператора return (строка 3), который завершает выполнение функции, в которой он задан, и возвращает управление в вызывающую функцию. Значение выражения, стоящего за оператором return, возвращается в вызывающую функцию в качестве значения вызываемой функ-
7.4. Проект «Multfile» 473 ции (см. Оператор return). Таким образом, функция fund возвращает удвоенное значение фактического параметра (а+а); func2 — квадрат фактического параметра (аха); func3 — утроенное значение фактического параметра (ахЗ). В строке 4 закрывающая фигурная скобка указывает конец тела соответствую- щей функции. Главный файл этого проекта — mainfile.c, т. к. он содержит основную функ- цию программы (main). Текст этого файла с русифицированными комментариями: 1: /* 2: Пример простого многофайлового проекта 3: 4: CodeVisionAVR С Compiler 5: (С) 2000-2002 HP InfoTech S.R.L. б: www.hpinfotech.ro 7: */ 8: 9: // это основной файл проекта 10: 11: // подключим заголовочные файлы с объявлениями функций 12: #include "filel.h" 13: #include "file2.h" 14: tfinclude "file3.h" 15: 16: main() 17 :{ 18: // объявим локальную переменную 19: int i; 20: // вызовем функцию с прототипом, объявленным в "filel.h" 21: // сама функция расположена в "filel.c" 22 : i = funcl(10); 23: // вызовем функцию с прототипом, объявленным в "file2.h" 24: // сама функция расположена в "file2.c" 25 : i = func2(i); 26: // вызовем функцию с прототипом, объявленным в "file3.h" 27: // сама функция расположена в "file3.c" 28 : i = func3(i) ; 29: // здесь остановка 30: while (1); 31: } В этом файле знаками /* (начало) и */ (конец) выделен блок с комментария- ми (строки 1...7), а знаком // — строки с комментариями (см. Комментарии). Директивами #include (строки 12...14) подключаются заголовочные файлы filel.h, filel.h и file3.h. Перед компиляцией препроцессор компилятора вставит вместо этих строк текст соответствующих файлов (см. Директива ^include). Строки 16...31 — это определение основной функции программы (main). Эта функция обязательно должна присутствовать во всех программах (но только од- на!). Именно с этой функции, в каком бы месте программы она ни находилась, начинается выполнение программы.
474 Глава 7. Примеры проектов В строке 16 осуществляется объявление функции main. Вначале опущено (но подразумевается) ключевое слово void, указывающее на то, что эта функция не воз- вращает никаких значений. Затем следует имя функции main (его изменять нельзя!). В скобках опущено (но подразумевается) ключевое слово void, которое указывает на то, что в эту функцию не передаются никакие параметры (см. Функции). В строке 17 открывающая фигурная скобка указывает начало тела функции main. В строке 19 объявляется переменная i типа int (целое) (см. Типы данных). В строках 22, 25 и 28 последовательно осуществляется вызов функций fund, fund и func3 с фактическими параметрами. Сами функции расположены соот- ветственно в файлах filel.c, filel.c, file3.c. Эти файлы, как и заголовочные файлы filel.h, filel.h, file3.h, подключены к проекту, но не директивой #include, а с помо- щью средств IDE CodeVisionAVR при конфигурировании данного проекта (Рис. 7.7). Рис. 7.7. Добавление в проект файлов filel.c, filel.c, ШеЗ.с. Подробнее о добавлении файлов в проект см. Закладка Files (Файлы). У этого способа есть существенный недостаток: при распечатке текста глав- ного файла проекта не видно, какие ещё файлы добавлены в проект (если это не указано в комментариях). Перед компиляцией к тексту главного файла проекта в конце будет добавлен текст файлов filel.c, filel.c, file3.c. Это можно увидеть при моделировании проекта bAVR Studio (Рис. 7.8). В строке 30 файла mainfile.c реализован бесконечный цикл с помощью опера- тора while (см. Оператор while). Этот оператор будет выполняться до тех пор, пока выражение в скобках истинно, т. е. не 0. Выражение в скобках — 1 (можно также написать 6 или 24), а не 0, т. е. всегда истинно. Следовательно, этот оператор бу- дет выполняться бесконечно. В строке 31 закрывающая фигурная скобка указывает конец тела функции main. Программа работает следующим образом. После подачи питания (или аппа- ратного сброса на выводе RESET микроконтроллера) начинает выполняться функция main. При этом объявляется переменная i и осуществляется вызов фун- кции fund с фактическим параметром 10. Функция fund возвращает удвоенное значение фактического параметра (10+10 = 20). Это значение присваивается пе- ременной i. Затем осуществляется вызов функции fund с фактическим парамет- ром i, который после предыдущей операции равен 20. Функция fund возвращает
7.4. Проект «Multfile» 475 квадрат фактического параметра (20x20 = 400). Это значение присваивается пе- ременной i. После этого осуществляется вызов функции func3 с фактическим па- раметром i, который после предыдущей операции равен 400. Функция fiinc3 воз- вращает утроенное значение фактического параметра (400x3=1200). Это значе- ние присваивается переменной i. В результате i = 1200. После этого программа переходит к выполнению бесконечного цикла while. Рис. 7.8. Добавленный текст файлов filel.с, filel.c, ШеЗ.с. В этом проекте файлы filel.с, filel.c, ШеЗ.с можно подключить с помощью ди- рективы #include, предварительно удалив их из конфигурации проекта с помощью кнопки Remove (Удалить) (см. Закладка Files (Файлы)): 10: 11: 12: 13: 14: 15: 16: tfinclude #include ttinclude #include #include ^include filel.h file2.h file3.h filel.c file2.c file3.c 17: Начинающим рекомендуется использовать именно этот способ, т. к. при рас- печатке текста главного файла проекта сразу видно, какие ещё файлы добавлены в проект.
476 Глава 7. Примеры проектов Перед компиляцией к тексту главного файла проекта будет добавлен текст файлов file 1 .с, fileZ.c, ГПеЗ.с сразу после соответствующей директивы #include, что конечно же нагляднее. Это можно увидеть при моделировании проекта в AVR Studio (Рис. 7.9). Сравните с Рис. 7.8. Рис. 7.9. Добавленный текст файлов filel.c, file2.c, ШеЗ.с при использовании директивы #include. 7.5. Проект «EEPROM» Этот пример демонстрирует доступ к внутренней EEPROM-памяти микро- контроллера. В этом проекте содержится один исходный файл — еергош.с. Текст этого фай- ла с русифицированными комментариями: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: // Пример доступа к EEPROM // CodeVisionAVR С Compiler // (С) 2000-2002 HP InfoTech S.R.L. // www.hpinfotech.ro // Чип: AT90S2313 // Модель памяти: TINY // Размер стека данных: 64 Б flash char f[]="This is a test"; #pragma warn- eeprom char e[16];
7.5. Проект «EEPROM» 477 13: tfpragma warn+ 14: 15: char г[16]; 16: 17: void main (void) 18: { 19: char flash *ptr_to_flash; 20: char eeprom *ptr_to_eeprom; 21: char *ptr_to_ram; 22: 23: // скопируем строку f из FLASH 24: // в строку e в EEPROM 25: tr_to_flash=f; 26: pptr_to_eeprom=e; 27: 28: while (*ptr_to_flash) 29: *ptr_to_eeprom++=*ptr_to_flash 30: 31: // скопируем строку e из EEPROM 32: // в строку г в SRAM 33: ptr_to_eeprom=e; 34: ptr_to_ram=r; 35: 36: while (*ptr_to_eeprom) 37: *ptr_to_ram++=*ptr_to_eeprom+ 38: 39: // здесь остановка 40: while (1); 41: ) В этом файле знаками // выделены строки с комментариями (строки 1...7), в которых приводится информация о проекте (см. Комментарии). В строке 9 объявляется и инициализируется строковая константа (символь- ный массив) f. Она располагается во FLASH-памяти микроконтроллера, на что указывает ключевое слово flash (см. Константы). Строковая константа f инициализируется строковым литералом «This is a test» (см. Инициализация данных). Напомним, что в конец каждого строкового литерала компилятор добавляет нулевой символ, т. е. строка рассматривается как массив символов, число элементов которого равно числу символов в строке плюс 1, так как нулевой символ (символ конца строки) также является элементом массива (см. Константы). Директивой #pragma warn- (строка 11) компилятору запрещается выдавать предупреждения (см. Директива ttpragma warn). При отсутствии этой директивы компилятор Си будет выдавать предупреждение о том, что EEPROM-память мик- роконтроллера не инициализирована. В строке 12 объявляется глобальный символьный массив е. Этот массив будет расположен в EEPROM, на что указывает ключевое слово eeprom. При этом все элементы этого глобального массива автоматически инициализируются со значе- нием 0 (см. Массивы).
478 Глава 7. Примеры проектов В строке 13 директивой #pragma warn+ компилятору вновь разрешается выда- вать предупреждения (см. Директива ^pragma warn), В строке 15 объявляется глобальный символьный массив г. Этот массив будет расположен в SRAM. При этом все элементы этого глобального массива автома- тически инициализируются со значением 0 (см. Массивы), Строки 17...41 — это определение основной функции программы (main). Эта функция обязательно должна присутствовать во всех программах (но только од- на!). Именно с этой функции, в каком бы месте программы она ни находилась, начинается выполнение программы. В строке 17 осуществляется объявление функции main. Вначале стоит ключе- вое слово void, указывающее на то, что эта функция не возвращает никаких зна- чений. Затем следует имя функции main (его изменять нельзя!) и ключевое слово void в скобках, которое указывает на то, что в эту функцию не передаются ника- кие параметры (см. Функции). В строке 18 открывающая фигурная скобка указывает начало тела функции main. В строке 19...21 объявляются указатели типа char (байт) для различных облас- тей памяти микроконтроллера (знак указателя — *). Указатель ptr_to_flash — для FLASH (на что указывает ключевое слово flash перед указателем), ptr to eeprom — для EEPROM (на что указывает ключевое слово eeprom перед указателем), ptrtoram — для SRAM (см. Указатели), Так как в настройках проекта на закладке С Compiler (Компилятор Си) выбра- на опция char is unsigned (байт является беззнаковым) (Рис. 7.10), то все данные Рис. 7.10. Опция char is unsigned (байт является беззнаковым) выбрана.
7.5. Проект «EEPROM» 479 типа char (байт) компилятор будет обрабатывать, как данные типа unsigned char (беззнаковый байт) (см. Закладка С Compiler (Компилятор Си)), т. е. массивы f (строка 9), е (строка 12) и г (строка 15) будут состоять из данных типа unsigned char (беззнаковый байт), а указатели ptr_to_flash, ptr_to_eeprom и ptr_to_ram (строки 19...21) будут иметь тип unsigned char (беззнаковый байт). Настоятельно рекомендуется явно определять тип данных в тексте про- граммы, т. к. при распечатке текста программы настроек проекта не видно. Это может вызвать неправильное понимание работы программы! В строке 25 указатель ptr_to_flash устанавливается на строку f, т. е. переменной ptr_to_flash присваивается значение адреса самого первого члена массива f, рас- положенного во FLASH. В строке 26 указатель ptr_to_eeprom устанавливается на массив е, т. е. пере- менной ptr_to_eeproni присваивается значение адреса самого первого члена мас- сива е, расположенного в EEPROM. В строке 28 находится оператор цикла while. Выражение в скобках означает, что оператор while будет выполняться, пока значение, на которое установлен ука- затель ptr_to_flash, не 0, т. е. пока он не будет установлен на последний символ строки (на символ с кодом 0x00). В строке 29 находится оператор присваивания, с помощью которого осущест- вляется запись в EEPROM. Он интерпретируется следующим образом: считать значение из FLASH по адресу, на который установлен указатель ptr_to_flash, и за- писать (присвоить) это значение в EEPROM по адресу, на который установлен указатель ptr_to_eeprom. Только после этого (т. к. знак операции инкремента ++ записан после операн- да) увеличить оба указателя на 1 (см. Унарные операции). Таким образом, происходит копирование строки f из FLASH в строку е в EEPROM. Так как любой строковый литерал заканчивается 0 (точнее, символом конца строки, код которого 0x00), то цикл while будет выполняться до тех пор, по- ка не будет скопирована вся строка. Аналогично происходит копирование строки е из EEPROM в строку г в SRAM. В строке 33 указатель ptr_to_eeproni вновь устанавливается на массив е, т. е. пе- ременной ptr_to_eeproni присваивается значение адреса первого члена массива е, расположенного в EEPROM. Теперь уже после выполнения операторов 25...29 в этот символьный массив записана строка «This is a test» из FLASH. В строке 34 указатель ptr_to_rani устанавливается на массив г, т. е. переменной ptr_to_ram присваивается значение адреса первого члена массива г, расположен- ного в SRAM. В строке 36 находится оператор цикла while. Выражение в скобках означает, что оператор while будет выполняться, пока значение, на которое установлен ука- затель ptr_to_eeprom, не 0, т. е. пока он не будет установлен на последний символ строки (на символ, код которого 0x00). В строке 37 находится оператор присваивания, с помощью которого осущест- вляется запись в SRAM. Он интерпретируется следующим образом: считать зна- чение из EEPROM по адресу, на который установлен указатель ptr_to_eeproni, и
480 Глава 7. Примеры проектов записать (присвоить) это значение в SRAM по адресу, на который установлен ука- затель ptr_to_ram. Только после этого (т. к. ++ записан после операнда) увеличить оба указателя на 1 (см. Унарные операции). Таким образом, происходит копирование строки е из EEPROM в строку г в SRAM, т. к. любой строковый литерал заканчивается 0, то цикл while будет выпол- няться до тех пор, пока не будет скопирована вся строка. В строке 40 реализован бесконечный цикл с помощью оператора while (см. Оператор while). Этот оператор будет выполняться до тех пор, пока выраже- ние в скобках истинно, т. е. не 0. Выражение в скобках — 1 (можно также напи- сать 16 или 75), а не 0, т. е. всегда истинно. Следовательно, этот оператор будет выполняться бесконечно. В строке 41 закрывающая фигурная скобка указывает конец тела функции main. Для получения более подробной информации см. Доступ к EEPROM. Программа работает следующим образом. После подачи питания (или аппа- ратного сброса на выводе RESET микроконтроллера) начинает выполняться функция main. При этом объявляются указатели для различных областей памяти микроконтроллера: FLASH, EEPROM и SRAM. Затем с помощью этих указателей и оператора цикла while осуществляется копирование строки «This is a test» снача- ла из FLASH-памяти в EEPROM, а затем из EEPROM в SRAM. После этого про- грамма переходит к выполнению бесконечного цикла while. Обратите внимание, что в данном проекте копирование строк происходит без последнего нулевого символа. Это может привести к нежелательным результатам. Например, т. к. информация в EEPROM-памяти не пропадает при снятии напря- жения питания, то, если не обнулить её содержимое (не записать во все ячейки значение 0x00 при программировании чипа), могут остаться какие-либо данные. Тогда после записи строки «This is a test» в EEPROM следующее после этой стро- ки значение может оказаться не 0x00. В этом случае при копировании строки из EEPROM в SRAM цикл while будет продолжаться до тех пор, пока не встретится значение 0x00. Таким образом, в SRAM, кроме строки «This is a test», может быть скопирована и другая, ненужная информация. Чтобы избежать этого, в текст программы после каждого цикла while, с помо- щью которых осуществляется копирование строк, следует вставить ещё по одно- му опереатору присваивания: 22: 23: // скопируем строку f из FLASH 24: //в строку е в EEPROM 25: ptr_to_flash=f; 26: ptr_to_eeprom=e; 27: 28: while (*ptr_to_flash) 29: *ptr_to_eeprom+ + = *ptr_to_flash++; 30: 31: // скопируем нулевой символ в конце строки f из FLASH 32: //в конец строки е в EEPROM
7.6. Проект «Lcddemo» 481 33: *ptr_to_eeprom=*ptr_to_flash; 34: 35: 36: // скопируем строку е из EEPROM // в строку г в SRAM 37: ptr_to_eeprom=e; 38: ptr_to_ram=r; 39: 40: while (*ptr_to_eeprom) 41: *ptr_to_ram++=*ptr_to_eeprom++; 42: 43: // скопируем нулевой символ в конце строки е из EEPROM 44: // в конец строки е в SRAM 45: *ptr_to_ram=*ptr_to_eeprom; 46: 47: // здесь остановка 48: while (1); 49: ) После выхода из соответствующих циклов while указатели ptr_to_flash и ptr_to_eeprom установлены на нулевой символ, которым оканчивается строковый литерал во FLASH и EEPROM соответственно. Теперь в строках 33 и 45 операто- ры присваивания перепишут нулевой символ, которым оканчивается строковый литерал «This is a test», из FLASH в EEPROM (строка 33) и из EEPROM в SRAM (строка 45). 7.6. Проект «Lcddemo» Этот проект демонстрирует вывод информации на LCD-модуль. В этом проекте содержится один исходный файл — lcddemo.с. Текст этого файла с русифицированными комментариями: 1: /* 2: 3: Демонстрируется LCD 4: 5: CodeVisionAVR С Compiler (С) 2000-2002 HP InfoTech S.R.L. б: 7: www.hpinfotech.ro 8: Чип: AT90S8515 9: Модель памяти: SMALL 10: Размер стека данных: 128 Б 11: 12: Используется алфавитно-цифровой LCD 2x16, подключённый 13: к разъёму PORTC STK500 следующим образом: 14: 15: [LCD] [Разъём PORTC STK500] 1 GND - 9 GND 16: 2 +5V - 10 VCC 17: 3 VLC - управление контрастностью LCD напряжение 0...1B 18: 4 RS - 1 PC0 19: 5 RD - 2 PCI
482 Глава 7. Примеры проектов 20: 6 EN - 3 РС2 21: 11 D4 - 5 РС4 22: 12 D5 - 6 РС5 23: 13 D6 - 7 РС6 24: 14 D7 - 8 РС7 25: */ 26: 27: 28: // LCD подключён к выходам PORTC // смотрите файл led.h в поддиректории .\inc 29: #asm 30: .equ lcd_port=0xl5 ;PORTC 31: tfendasm 32: 33: // подключим программы управления LCD 34: tfinclude <lcd.h> 35: 36: void main(void) 37: 38: { // инициализируем LCD для 39: // 2 строк и 16 столбцов 40: lcd_init(16); 41: 42: // перейдём на вторую строку LCD 43: lcd_gotoxy(0,1); 44: 45: // отобразим сообщение 46: lcd_putsf("Hello world"); 47: 48: // здесь остановка 49: 50: while (1); } В этом файле знаками /* (начало) и */ (конец) выделен блок с комментария- ми, а знаком // — строки с комментариями (см. Комментарии). В блоке с комментариями (строки 1 ...25) содержится информация о данном проекте и даются рекомендации по настройке платы STK500 при аппаратной реа- лизации данного проекта. В строках 29...31 объявляется, какой порт микроконтроллера будет использо- ван для связи с модулем LCD (подробнее см. LCD-функции для дисплеев до 2*40 символов). Для этого в программу включён ассемблерный код. Директива #asm (строка 29) говорит компилятору о начале ассемблерного кода, а директива #endasm (строке 31) — о его завершении (см. Директивы #asm и ttendasm). В строке 30 ассемблерная директива .equ присваивает идентификатору_lcd_port значение 0x15. Это значение, соответствующее адресу регистра PORTx выбранного порта (в данном случае выбран Порт С), находим в файле 90s8515.h в соответствующей строке: «sfrb PORTC=Oxl5;». Этот файл находится в поддиректории ..\INC. При трансляции ассемблерного кода, полученного при компиляции данного проекта компилятором Си CodeVisionAVR, ассемблер вместо этого идентифика- тора подставит его значение.
7.6. Проект «Lcddemo» 483 В случае модификации программы при использовании другого порта для под- ключения LCD-модуля достаточно будет лишь заменить значение в указанной директиве .equ, не изменяя остального текста программы. Обратите внимание, что комментарии в ассемблерной части кода отде- ляются точкой с запятой (;). Иначе будет выдаваться ошибка при ассем- блировании (при построении проекта). Подробнее о директивах ассемблера см. систему помощи бесплатной про- граммы AYR Studio от Atmel, которую можно скачать на сайте разработчиков мик- роконтроллеров AVR http://www.atmel.com. Директивой #include (строка 34) подключаются LCD-функции (см. LCD-функции для дисплеев до 2x40символов). Перед компиляцией препроцессор компилятора вставит вместо этой строки текст файла led.h (см. Директива ^include). Строки 36...50 — это определение основной функции программы (main). Эта функция обязательно должна присутствовать во всех программах (но только од- на!). Именно с этой функции, в каком бы месте программы она ни находилась, начинается выполнение программы. В строке 36 осуществляется объявление функции main. В начале стоит ключе- вое слово void, указывающее на то, что эта функция не возвращает никаких зна- чений, затем следует имя функции main (его изменять нельзя!) и ключевое слово void в скобках, которое указывает на то, что в эту функцию не передаются ника- кие параметры (см. Функции). В строке 37 открывающая фигурная скобка указывает начало тела функции main. В строке 40 осуществляется вызов функции lcd_init с фактическим парамет- ром 16 (количество столбцов в используемом модуле LCD). Эта функция инициа- лизирует модуль LCD, очищает дисплей и устанавливает позицию для вывода символа в ряд 0 столбца 0. Курсор не отображается. В строке 43 осуществляется вызов функции lcd_gotoxy с фактическими пара- метрами (0, 1). Эта функция устанавливает текущую позицию дисплея соответ- ственно в ряд 1 столбца 0. Нумерация рядов и столбцов начинается с 0. В строке 46 осуществляется вызов функции lcd_putsf, которая отображает в текущей позиции дисплея последовательность «Hello world». Функции lcd_init, lcd_gotoxy и lcd_putsf определены в подключённом файле led.h (строка 34). Подробнее об этих функциях см. LCD-функции для дисплеев до 2*40 символов. В строке 49 реализован бесконечный цикл с помощью оператора while (см. Оператор while). Этот оператор будет выполняться до тех пор, пока выраже- ние в скобках истинно, т. е. не 0. Выражение в скобках — 1 (можно также напи- сать 2 или 18), а не 0, т. е. всегда истинно. Следовательно, этот оператор будет вы- полняться бесконечно. В строке 50 закрывающая фигурная скобка указывает конец тела функции main. Программа работает следующим образом. После подачи питания (или аппа- ратного сброса на выводе RESET микроконтроллера) начинает выполняться функция main. Последовательно вызываются функция lcd_init, которая инициа-
484 Глава 7. Примеры проектов лизирует LCD-модуль, функция led gotoxy, которая устанавливает текущую пози- цию дисплея, и функция Icd_putsf, которая в текущую позицию дисплея LCD вы- водит последовательность «Hello world». После этого программа переходит в бесконечный цикл. Результат выполнения этой программы представлен на Рис. 7.11. Цепь сбро- са, питания и кварцевый резонатор микроконтроллера не показаны (см. Рис. 4.2). DD1 AT90S8515 XTAL1 XTAL2 RESET РВО/ТО РВ1/Т1 PB2/AIN0 PB3/AIN1 PB4/SS PB5/MOSI PB6/MIS0 PB7/SCK PDO/RXD PD1/TXD PD2/1NT0 PD3/INT1 PD4 PD5/OC1A PD6/WR PD7/RD PAO/ADO PA1/AD1 PA2/AD2 PA3/AD3 PA4/AD4 PA5/AD5 PA6/AD6 PA7/AD7 РС0/А8 РС1/А9 РС2/А10 РСЗ/А11 РС4/А12 РС5/А13 РС6/А14 РС7/А15 ОС1В ICP 39 38 37 36 35 34 33 21 22 23 24 25 26 27 30 29 LCD1 VCC RV1 10k GND Рис. 7.11. Результат работы программы — выведенная строка «Hello world». При отсутствии отладочной платы STK500 можно самостоятельно реализо- вать этот проект по приведённой схеме. 7.7. Проект «Lcdchar» Этот проект демонстрирует определение пользователем собственных симво- лов для LCD-модуля и их вывод на дисплей. В этом проекте содержится один исходный файл — Icdchar.c. Текст этого фай- ла с русифицированными комментариями: 1: /* 2: 3: 4: 5: Демонстрируются символы LCD, определённые пользователем CodeVisionAVR С Compiler (С) 2000-2002 HP InfoTech S.R.L. 6: 7 : www.hpinfotech.ro 8: Чип: AT90S8515 9: Модель памяти: SMALL 10: Размер стека данных: 128 Б 11: 12: Используйте алфавитно-цифровой LCD 2x16, 13: подключённый к разъёму LCD STK200/300. 14: Связи между LCD и платой должны быть
7.7. Проект «Lcdchar» 485 15: по возможности короче. 16: */ 17: 18: // подключим программы управления LCD для STK200/300 19: tfinclude <lcdstk.h> 20: 21: typedef unsigned char byte; 22: 23: // таблица для символа, определённого пользователем 24: // стрелка, указывающая на правый верхний угол 25: flash byte char0[8]= 26: { 27: ObOOOOOOO, 28: ObOOOllll, 29: ObOOOOOll, 30: ObOOOOlOl, 31: ObOOOlOOl, 32: ObOOlOOOO, 33: ObOlOOOOO, 34: OblOOOOOO 35: }; 36: 37: 38: // функция, используемая для определения символов пользователя void define_char(byte flash *pc,byte char_code) 39: 40: { byte i,a; 41: a= (char_code«3) 10x40; 42: for (i=0; i<8; i++) lcd_write_byte(a++,*pc++); 43: ) 44: 45: void main(void) 46: { 47: // инициализируем LCD для 48: //2 строк и 16 столбцов 49: lcd_init(16); 50: 51: // определим символ 0 пользователя 52: define_char(charO,0); 53: 54: // переключимся на запись в RAM дисплея 55: lcd_gotoxy(0,0); 56: // отобразим символы, определённые пользователем 57: lcd_putsf("User defined\nchar 0:"); 58: // отобразим символ 0, определённый пользователем 59: lcd_putchar(0); 60: 61: // здесь остановка 62: while (1); 63: }
486 Глава 7. Примеры проектов В этом файле знаками /* (начало) и */ (конец) выделен блок с комментария- ми, а знаком // — строки с комментариями (см. Комментарии). В блоке с комментариями (строки 1... 16) содержится информация о данном проекте и даются рекомендации по настройке платы STK200/300 при аппаратной реализации данного проекта. Директивой #include (строка 19) подключаются LCD-функции (см. LCD-функции для дисплеев, подключённых в режиме отображения 8-битовой памяти). Перед компиляцией препроцессор компилятора вставит вместо этой строки текст файла Icdstk.h (см. Директива ^include). В строке 21 объявляется тип данных byte, который соответствует типу данных unsigned char (беззнаковый символьный). Запись byte короче и нагляднее. Под- робнее см. Определение типов данных. В данном случае оператор typedef удобен, но вовсе не обязателен. В строках 25...35 задаётся массив (таблица) из 8 членов для определения сим- вола пользователя (стрелка, указывающая на правый верхний угол). Этот массив располагается во FLASH-памяти микроконтроллера, на что указывает ключевое слово flash. Подробнее об определении символов пользователя см. LCD-функции. Строки 38...43 — это определение функции define_char, которая служит для определения символов пользователя. В строке 38 осуществляется объявление функции define_char. Вначале стоит ключевое слово void, указывающее на то, что эта функция не возвращает никаких значений. Далее следует имя функции define_char (может быть произвольным). По этому имени далее в программе осуществляется вызов данной функции. В скобках находится список формальных параметров (см. Функции). Первый параметр — это указатель (на что указывает значок *) с именем рс на константу, расположенную во FLASH-памяти микроконтроллера (на что указы- вает ключевое слово flash). Тип указателя — byte (тип byte определён в строке 21). Второй параметр — это переменная char_code типа byte. В этой переменной будет храниться код (номер) символа. В строке 39 открывающая фигурная скобка указывает начало тела функции define_char. В теле функции define_char сначала объявляются переменные i и а типа byte (строка 40). В переменной i будет храниться номер текущего байта символа, опре- делённого пользователем. Всего под символ отводится 8 Б, т. е. переменная i бу- дет принимать значения от 0 до 7. В переменной а будет храниться код команды установки адреса самого младшего байта символа с номером char_code в CGRAM. Код команды вычисляется следующим образом. Так как под каждый символ отводится 8 Б, то байты 0-го символа имеют адреса 0x00...0x07, байты 1-го — 0x08...0x0F,..., байты п-го — (пх8) — (пх8+7). То есть чтобы получить адрес само- го младшего байта символа, надо номер (код) символа (нумерация начинается с 0) умножить на 8 (или сдвинуть на 3 бита влево (<<), что эквивалентно). Чтобы получить код команды установки адреса в CGRAM, следует установить бит 6 в 1, а 7 — в 0 (см. Табл. 5.35). Это можно сделать, выполнив операцию побитного ИЛИ (знак операции — |) полученного адреса с числом 0х40=0Ь 0100 0000, т. к. для указания адреса ячеек CGRAM в коде команды отводится 6 младших битов (см. Табл. 5.35). Именно это и делается в строке 41.
77. Проект «Lcdchar» 487 В строке 42 находится оператор for (см. Оператор for). Тело этого оператора будет выполняться 8 раз (от i = 0 до i = 7). В теле оператора for вызывается функ- ция lcd_write_byte, которая записывает байт, находящийся по указателю *рс во FLASH-памяти микроконтроллера, в ОЗУ знакогенератора (CGRAM) или в DDRAM LCD по адресу, который определяется переменной а (см. LCD-функции для дисплеев, подключённых в режиме отображения 8-битовой памяти). После этого значения указателя *рс и переменной а увеличиваются на 1 (т. к. знак опе- рации инкремента ++ записан после операнда, см. Унарные операции). Таким об- разом, все 8 байтов символа, определённого во FLASH-памяти микроконтролле- ра, переписываются в ОЗУ знакогенератора (CGRAM) LCD. Функция lcd_write_byte определена в подключённом файле Icdstk.h (строка 19). В строке 43 закрывающая фигурная скобка указывает окончание тела функ- ции define_char. Строки 45...63 — это определение основной функции программы (main). Эта функция обязательно должна присутствовать во всех программах (но только ОДНА!). Именно с этой функции, в каком бы месте программы она ни находи- лась, начинается выполнение программы. В строке 45 осуществляется объявление функции main. Вначале стоит ключе- вое слово void, указывающее на то, то эта функция не возвращает никаких значе- ний. Затем следует имя функции main (ЕГО ИЗМЕНЯТЬ НЕЛЬЗЯ!) и ключевое слово void в скобках, которое указывает на то, что в эту функцию не передаются никакие параметры (см. Функции). В строке 46 открывающая фигурная скобка указывает начало тела функции main. В строке 49 осуществляется вызов функции lcd_init с фактическим парамет- ром 16 (количество столбцов в используемом модуле LCD). Эта функция инициа- лизирует модуль LCD, очищает дисплей и устанавливает позицию для вывода символа в ряд 0 и столбец 0. Курсор не отображается. Эта функция определена в подключённом файле Icdstk.h (строка 19). В строке 52 осуществляется вызов функции define_char с фактическими пара- метрами charO и 0. То есть указатель ставится на самый младший член массива charO, который определён в строке 25, а символ, определённый пользователем, будет иметь код (номер) 0 в CGRAM LCD. Таким образом, весь символ, опреде- лённый в массиве charO во FLASH-памяти микроконтроллера, переписывается в ячейки памяти 0x00...0x07 в CGRAM LCD. В строке 55 осуществляется вызов функции lcd_gotoxy с фактическими пара- метрами (0, 0). Эта функция устанавливает текущую позицию дисплея соответс- твенно в столбец 0 и ряд 0. Нумерация рядов и столбцов начинается с 0. Эта функция определена в подключённом файле Icdstk.h (строка 19). В строке 57 осуществляется вызов функции lcd_putsf, которая отображает в те- кущей позиции дисплея последовательность «User defined\nchar 0:», т. е. сначала в верхней строке LCD будет выведена последовательность «User defined», затем курсор будет переведён на новую строку (последовательность \п), и в новой стро- ке будет выведена последовательность «char 0:».
488 Глава 7. Примеры проектов В строке 59 осуществляется вызов функции lcd__putchar, которая отображает символ с кодом 0 в текущей позиции дисплея. Так как код 0 имеет символ, опре- делённый ранее пользователем, то именно он и будет выведен в текущей позиции дисплея. Обратите внимание, что этот символ дублируется ещё и кодом 0x08 (см. Табл. 5.32), т. е. символ пользователя также можно вывести коман- дой: led_putchar (0x08); Функции lcd_gotoxy, lcd_putsf и lcd__putchar определены в подключённом фай- ле Icdstk.h (строка 19). Подробнее об этих функциях см. LCD-функции для диспле- ев, подключённых в режиме отображения 8-битовой памяти. В строке 62 реализован бесконечный цикл с помощью оператора while (см. Оператор while). Этот оператор будет выполняться до тех пор, пока выражение в скобках истинно, т. е. не 0. Выражение в скобках — 1 (можно также написать 27 или 9), а не 0, т. е. всегда истинно. Следовательно, этот оператор будет выпол- няться бесконечно. В строке 63 закрывающая фигурная скобка указывает конец тела функции main. См. также пример в главе LCD-функции для дисплеев, подключённых в режиме отображения 8-битовой памяти. Программа работает следующим образом. После подачи питания (или аппа- ратного сброса на выводе RESET микроконтроллера) начинает выполняться функция main. Последовательно вызываются функция ledjnit, которая инициа- лизирует LCD-модуль, и функция define_char, которая определяет символ пользо- вателя с кодом 0 в LCD. Затем вызываются функция lcd_gotoxy, которая устанав- ливает текущую позицию дисплея, и функция lcd_putsf, которая в верхнюю стро- ку LCD, начиная с текущей позиции дисплея, выводит последовательность «User defined», а в нижнюю — «char 0:». После этого вызывается функция lcd__putchar, которая в текущую позицию дисплея выводит символ, определённый ранее поль- зователем. После этого программа переходит к выполнению бесконечного цикла while. Результат выполнения этой программы представлен на Рис. 7.12. Цепь сбро- са, питания и кварцевый резонатор микроконтроллера не показаны (см. Рис. 4.2). При отсутствии отладочной платы STK200/300 можно самостоятельно реали- зовать этот проект по приведённой схеме. 7.8. Проект «Keypad» Этот пример демонстрирует работу микроконтроллера с матричной клавиату- рой. Микроконтроллер определяет нажатую кнопку клавиатуры и выводит её код на алфавитно-цифровой LCD-модуль.
7.8. Проект «Keypad» 489 LCD1 WR 1 User defined char DD1 AT90S8515 XTAL1 XTAL2 RESET PBO/TO PB1/T1 PB2/AIN0 PB3/AIN1 PB4/SS PB5/MOSI PB6/MISO PB7/SCK PDO/RXD PD1/TXD PD2/INT0 PD3/INT1 PD4 PD5/OC1A PD6/WR PD7/RD PAO/ADO PA1/AD1 PA2/AD2 PA3/AD3 PA4/AD4 PA5/AD5 PA6/AD6 PA7/AD7 PC0/A8 PC1/A9 PC2/A10 PC3/A11 PC4/A12 PC5/A13 PC6/A14 PC7/A15 ALE OC1B ICP 39 38 37 36 35 34 33 21 22 23 24 25 26 27 30 29 7400 Puc. 7.12. Результат работы программы — выведен символ, определённый ранее пользователем. В этом проекте используется матричная клавиатура, схема которой приведена на Рис. 7.13. Рис. 7.13. Матричная клавиатура — схема и назначение выводов. При нажатии определённой кнопки клавиатуры соответствующий горизон- тальный ряд (А, В, С или D) замыкается с соответствующим вертикальным столб- цом (1, 2, 3 или 4). Например, при нажатии кнопки «6» между собой замкнутся ряд В и столбец 3.
490 Глава 7. Примеры проектов В этом проекте содержится один исходный файл — keypad.с. Текст этого фай- ла с русифицированными комментариями: 1: /* 2: Демонстрируется клавиатура 4x4 3: 4: CodeVisionAVR С Compiler 5: (С) 2000-2002 HP InfoTech S.R.L. б: www.hpinfotech.ro 7: 8: Чип: AT90S8515 9: Подсоедините матричную клавиатуру следующим образом: 10: 11: [Разъём PORTD STK500] [КНОПКИ]R1 12: 1 PD0 0 1 2 3 о+5В 13: 1 1 1 IR2 1 14: 2 PD1 4 5 б 7 15: 1 1 1 IR3 1 16: 3 PD2 8 9 10---11 17: 1 1 1 IR4 I 18: 4 PD3 12---13---14---15 19: DI 1 1 | | 20: 5 PD4 -|<|- | 1 I 21: D2 III 22: б PD5 -|<| I 1 23: D3 I I 24: 7 PD6 -|<| 1 Rl...R4 = 10k...47k 25: D4 1 26: 8 PD7 -|<| D1...D4 = 1N4148 27: 28: Используйте цифробуквенный LCD 2x16, подключённый 29: к разъёму PORTC STK500 следующим образом: 30: 31: [LCD] [Разъём PORTC STK500] 32: 1 GND - 9 GND 33: 2 +5V - 10 VCC 34: 3 VLC - управление контрастностью LCD напряжение 0...1В 35: 4 RS - 1 РСО 36: 5 RD - 2 PCI 37: 6 EN - 3 РС2 38: 11 D4 - 5 РС4 39: 12 D5 - б РС5 40: 13 D6 - 7 РСб 41: 14 D7 - 8 РС7 42: */ 43: #asm 44: .equ lcd_port=0xl5 45: #endasm 46: 47: #include <lcd.h>
7.8. Проект «Keypad» 491 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: #include <stdio.h> ttinclude <delay.h> #include <90s8515.h> // частота кварца [Гц] ttdefine F_XTAL 3686400L // PIND0...3 будут входами рядов ttdefine KEYIN PIND // PORTD4...7 будут выходами столбцов ttdefine KEYOUT PORTD // используем для инициализации счёта таймера/счётчика 0 ttdefine INIT.TIMER0 TCNT0=0xl00L-F_XTAL/64L/500L ttdefine FIRST-COLUMN 0x80 ^define LAST-COLUMN 0x10 typedef unsigned char byte; // сохраним здесь состояние каждой кнопки как бит, // бит 0 будет KEY0, бит 1 KEY1,... unsigned keys; // буфер LCD-дисплея char buf[33]; // прерывание по переполнению таймера/счётчика 0 каждые 2 мс interrupt [TIM0_OVF] void timer0_int(void) { static byte key_pressed_counter=20; static byte key_released_counter,column=FIRST_COLUMN; static unsigned row_data,crt_key; // заново инициализируем таймер/счётчик 0 INIT.TIMER0; row_data<<=4; // получим группу 4 кнопок в переменной row_data row_dataI=~KEYIN&0xf; column>>=l; if (column— (LAST_COLUMN>> 1) ) { column=FIRST-COLUMN; if (row_data==0) goto new_key; if (key_released_counter) --key_released_counter; else { if (--key_pressed_counter==9) crt_key=row_data; else { if (row_data!=crt_key) {
492 Глава 7. Примеры проектов 98: new_key: 99: key_pressed_counter=10; 100: key_relec.sed_counter=0 ; 101: goto end_key; 102: }; 103: if (!key_pressed_counter) 104: 105: { keys=row„data; 106: key_released_counter=20; 107: }; 108: 109: }; }; 110: end_key:; 111: row_data=0; 112: }; 113: // выберем следующий столбец, входы будут 114: // с подтягивающими резисторами 115: KEYOUT—column; 116: } 117: 118: 119: // проверим, была ли нажата кнопка unsigned inkey(void) 120: ( 121: unsigned k; 122 : 123: if (k=keys) keys=0; return k; 124: } 125: 126: void init_keypad(void) 127: ( 128: DDRD=0xf0; 129: INIT_TIMER0; 130: TCCR0=3; 131: TIMSK=2; 132: #asm("sei") 133: } 134: 135: main() 136: 137: { unsigned k; 138: init_keypad(); 139: lcd_init(16); 140: lcd_putsf("CVAVR Keypad"); 141: 142: // прочитаем кнопку и отобразим ее код 143: while (1) 144: ( 145: lcd_gotoxy(0,1); 146: if (k=inkey()) 147: {
7.8. Проект «Keypad» 493 148: sprintf(buf,"Key code=%Xh",k); 149: IccLputs(buf); 150: } 151: 152: else lcd_putsf("NO KEY "); delay_ms(500); 153: } 154: } В этом файле знаками /* (начало) и */ (конец) выделен блок с комментария- ми, а знаком // — строки с комментариями (см. Комментарии). В блоке с комментариями (строки 1 ...42) даются рекомендации по подключе- нию матричной клавиатуры и LCD-модуля к отладочной плате STK500 при аппа- ратной реализации данного проекта. При отсутствии отладочной платы STK500 можно самостоятельно реализо- вать этот проект по схеме, приведённой на Рис. 7.14. Цепь питания, сброса и кварцевый резонатор микроконтроллера не показаны (см. Рис. 4.2). Рис. 7.14. Проект «Keypad». Схема электрическая принципиальная. R1...R4 = 10k...47k VD1...VD4= 1N4148 (КД521, КД522) В строках 43...45 объявляется, какой порт микроконтроллера будет использо- ван для связи с модулем LCD (см. LCD-функции для дисплеев до 2x40 символов). Для этого в программу включён ассемблерный код. Директива #asm (строка 43) гово- рит компилятору о начале ассемблерного кода, а директива #endasm (строка 45) — о его завершении (см. Директивы #asm и tiendasm). В строке 44 ассемблерная ди- ректива .equ присваивает идентификатору_lcd__port значение 0x15. Это значение, соответствующее адресу регистра PORTx выбранного порта (в данном случае вы- бран Порт С), находим в файле 90s8515.h в соответствующей строке: «sfrb PORTC=Oxl5;». Этот файл находится в поддиректории ..\INC.
494 Глава 7. Примеры проектов При трансляции ассемблерного кода, полученного при компиляции данного проекта компилятором Си CodeVisionAVR, ассемблер вместо этого идентифика- тора подставит его значение. В случае модификации программы при использовании другого порта для под- ключения LCD-модуля достаточно будет лишь заменить значение в указанной директиве .equ, не изменяя остального текста программы. Подробнее о директивах ассемблера см. систему помощи бесплатной про- граммы AYR Studio от Atmel, которую можно скачать на сайте разработчиков мик- роконтроллеров AVR http://www.atmel.com. Директивами #include (строки 47...50) подключаются: LCD-функции — файл led.h (подробнее см. LCD-функции для дисплеев до 2x40 символов)', функции вво- да/вывода — файл stdio.h (см. Стандартные функции ввода/вывода)', функции за- держки — файл delay.h (см. Функции задержки) и заголовочный файл для исполь- зуемого микроконтроллера AVR (AT90S8515) — файл 90s8515.h. Перед компиля- цией препроцессор компилятора вставит вместо этих строк текст соответствующих файлов (ам. Директива ^include). Директивами #define (строки 52...61) определяются идентификаторы F_XTAL, KEYIN, KEYOUT, INITTIMERO, FIRST-COLUMN и LAST_COLUMN. После этих строк перед компиляцией препроцессор компилятора заменит в тексте про- граммы эти идентификаторы на их значения: F_XTAL на 3686400L, KEYIN на PIND и т. д. В случае модификации программы для другой частоты кварца, для другого порта подключения клавиатуры и т. д. достаточно будет лишь заменить значения в указанных директивах #define, не изменяя остального текста програм- мы (см. Директива ttdefine, #undef). Директива #defme очень удобна, но вовсе не обязательна. В строке 63 объявляется тип данных byte, который соответствует типу данных unsigned char (беззнаковый символьный). Запись byte короче и нагляднее. Под- робнее см. Определение типов данных. Как и директива #define, оператор typedef удобен, но вовсе не обязателен. В строке 67 объявляется глобальная переменная keys типа unsigned int (беззна- ковое целое). Ключевое слово unsigned означает unsigned int (см. Типы данных). Глобальные переменные автоматически инициализируется со значением 0. В каждом бите этой переменной будет храниться состояние соответствующей кнопки клавиатуры. В бите 0 будет храниться состояние кнопки KEY0, в бите 1 — KEY1, ..., в бите 15 — KEY15. Значение 1 в соответствующем бите соответствует нажатой кнопке, значение 0 — отжатой. То есть если нажата кнопка KEY6, то значение переменной keys должно быть: ОЬ 0000 0000 0100 0000 = 0x0040. В строке 69 объявляется глобальный символьный массив (строковая перемен- ная) buf, состоящий из 33 членов. При этом все элементы этого массива автома- тически инициализируются со значением 0 (см. Массивы). В этом массиве будет храниться информация, предназначенная для вывода на LCD. Так как в настройках проекта на закладке С Compiler (Компилятор Си) выбра- на опция char is unsigned (байт является беззнаковым) (Рис. 7.15), то все данные типа char (байт) компилятор будет обрабатывать как данные типа unsigned char (беззнаковый байт) (см. Закладка С Compiler (Компилятор Си), т. е. массив buf бу- дет состоять из данных типа unsigned char (беззнаковый байт).
7.8. Проект «Keypad» 495 Рис. 7.15. Опция char is unsigned (байт является беззнаковым) выбрана. Настоятельно рекомендуется явно определять тип данных в тексте про- граммы, т. к. при распечатке текста программы настроек проекта не видно. Это может вызвать неправильное понимание работы программы! Строки 72... 116 — это определение программы (функции) обслуживания пре- рывания по переполнению таймера/счётчика 0 timerO_int. В строке 72 осуществляется доступ к системе прерываний микроконтроллера AVR, на что указывает ключевое слово interrupt. Далее за этим ключевым словом в квадратных скобках должен идти номер вектора прерывания. Но здесь вместо но- мера стоит идентификатор TIMO_OVF. Дело в том, что этот идентификатор опре- делён директивой #define в заголовочном файле 90s8515.h. Перед компиляцией препроцессор компилятора заменит TIMO_OVF на цифру 8 (номер вектора пре- рывания), как определено в файле 90s8515.h. Вместо TIMO_OVF в квадратных скобках можно было написать 8, но TIMO_OVF нагляднее. Далее идёт ключевое слово void, указывающее на то, что функция не возвращает никаких значений. Затем имя функции timerO_int (может быть произвольным, но лучше со смысло- вой нагрузкой). По этому имени далее в программе осуществляется вызов данной функции. Ключевое слово void в скобках указывает на то, что в эту функцию не передаются никакие параметры. Подробнее см. Использование прерываний.
496 Глава 7. Примеры проектов В строке 73 открывающая фигурная скобка указывает начало тела программы обслуживания прерывания. В строках 74...76 объявляются локальные переменные различных типов со спецификатором класса памяти static (статические). Такие переменные имеют глобальное время жизни и область видимости внутри функции, в которой они объявлены. При выполнении программы статические переменные инициализи- руются только один раз, даже если функция, где они инициализируются, вызыва- ется многократно (см. Переменные). Тип byte был определён ранее, в строке 63. Определение unsigned подразумевает тип unsigned int (беззнаковое целое) (см. Ти- пы данных). Некоторые переменные сразу инициализируются, т. е. им присваиваются оп- ределённые начальные значения. Если статические переменные специально не инициализированы, то при запуске программы они автоматически устанавлива- ются в 0. В строке 79 перед компиляцией препроцессор компилятора вместо иденти- фикатора INIT__TIMER0 подставит выражение, определённое директивой #define в строке 59. Таким образом, в этой строке происходит инициализация тайме- ра/счётчика 0, т. е. в его регистр TCNT0 записывается начальное значение, кото- рое вычисляется по формуле TCNT0 = 0xl00L-F__XTAL/64L/500L. В этой форму- ле перед компиляцией препроцессор заменит идентификатор F__XTAL на 3686400L (см. строку 53). Буква L в конце чисел означает, что эти константы име- ют тип long integer (длинное целое) (см. Константы). Сначала, согласно приоритетам операций (см. Приоритеты операций и поря- док вычислений), слева направо выполняются операции деления (знак операции /, приоритет 3). После этого выполняется операция вычитания (знак операции —, приоритет 4). Все вычисления ведутся с длинными целыми, чтобы не потерять значащие цифры. В заключение значение результата всех операций записывается (присваивается) в регистр TCNT0 (знак операции /, приоритет 14). В строке 80 осуществляется операция сдвига влево с присваиванием (знак операции — <<=) текущего значения переменной row_data. Значение сдвигается влево на 4 бита, т. е. значение 0-го бита перейдёт в 4-й бит, 1-го — в 5-й, ..., 11-го — в 15-й. Младшие 4 бита (с 0-го по 3-й) заполняются нулями (см. Бинар- ные операции). Таким образом, значение переменной row_data сдвигается на один ниббл (полубайт, или 4 бита) влево. Младший ниббл заполняется нулями. Полу- ченное значение присваивается переменной row_data. её значение станет равным ОЬ ХХХХ ХХХХ ХХХХ 0000, где ХХХХХХХХХХХХ — значение трех младших ниб- блов переменной row_data до операции. Значение самого старшего ниббла пере- менной row_data после этой операции пропадает. Выражение в строке 83 рассмотрим подробнее. Перед компиляцией препроцессор заменит идентификатор KEYIN на PIND (см. строку 55). PIND — это значение, считанное из регистра PINx Порта D, т. е. значения логических уровней, которые к настоящему времени присутствуют на физических (т. е. реальных) выводах Порта D (см. Доступ к регистрам ввода/вы- вода). В соответствии с приоритетами операций (см. Приоритеты операций и порядок вычислений) порядок вычисления выражения следующий. Сначала считывается
7.8. Проект «Keypad» 497 текущее значение PIND. После этого с этим значением выполняется операция побитового логического отрицания (знак операции приоритет 2). После её вы- полнения все 1 в значении PIND заменяются на 0, а 0 — на 1 (см. Унарные опера- ции). Затем выполняется операция побитного И (знак операции &, приоритет 8), полученного значения с числом Oxf = 0b 0000 1111. При этом каждый бит первого операнда сравнивается с соответствующим битом второго операнда. Если оба сравниваемых бита 1, то соответствующий бит результата устанавливается в 1, в противном случае — в 0 (см. Бинарные операции). В результате получается значе- ние 0Ь 0000 YYYY, где YYYY — значение младшего ниббла, т. е. битов 0...3-Й, регис- тра PIND, в котором 0 заменён на 1, а 1 — на 0. Последней осуществляется операция побитного ИЛИ с присваиванием (знак операции — |=, приоритет 14) текущего значения переменной row_data (0b ХХХХ ХХХХ ХХХХ 0000) и результата предыдущих операций (0Ь 0000 YYYY). При этом каждый бит первого операнда сравнивается с соответствующим битом второго. Если любой (или оба) из сравниваемых битов равен 1, то соответствующий бит результата устанавливается в 1, в противном случае — в 0 (см. Бинарные операции). Полученное значение (0Ь ХХХХ ХХХХ ХХХХ YYYY) присваивается переменной row_data. В строке 84 осуществляется операция сдвига вправо с присваиванием (знак операции »=) текущего значения переменной column. Значение сдвигается впра- во на 1 бит, т. е. значение 7-го бита переходит в 6-й бит, 6-го — в 5-й, ..., 1-го — в 0-й. Значение 0-го бита пропадёт, а в 7-й бит записывается 0. Полученное значе- ние присваивается переменной column. В строках 86...112 находится укороченный оператор if-else (без else) (см. Опе- ратор if-else). В строке 86 — начало этого оператора, на что указывает ключевое слово if. Вы- ражение в скобках (условие оператора if-else) вычисляется в следующем порядке. Сначала значение LAST__COLUMN (препроцессор подставит вместо него число 0x10 = 0Ь 0001 0000, см. строку 61) сдвигается вправо на 1 бит (знак операции »). Затем полученное значение (0x08 = 0Ь 0000 1000) сравнивается со значением пере- менной column, полученным в предыдущей строке программы. Если они равны — выражение истинно, если нет — ложно. Если выражение в скобках истинно (т. е. значение выражения отлично от ну- ля), то будет выполняться группа операторов (составной оператор), заключённая в фигурные скобки, находящиеся в строках 87 (открывающая скобка) и 112 (за- крывающая скобка). То есть будет выполняться тело оператора if-else. После это- го выполнение программы продолжается со строки 115. Если выражение в скоб- ках ложно (значение выражения равно нулю), то выполнение программы сразу переходит на строку 115, т. е. тело оператора if-else выполняться не будет. В составном операторе в строке 88 переменной column присваивается значе- ние FIRST__COLUMN (препроцессор подставит вместо него число 0x80 = 0Ь 1000 0000, см. строку 60). В строке 89 находится укороченный оператор if-else (без else). Так как в его те- ле всего один оператор, вся конструкция записана в одну строку, без фигурных скобок. В условии оператора if-else (в скобках) проверяется текущее значение пе- ременной row_data, и если оно равно 0 (равенство истинно), то выполняется one-
498 Глава 7. Примеры проектов ратор goto (см. Оператор goto), который осуществляет переход выполнения про- граммы на метку new_key (на строку 98). Если значение переменной row__data не равно 0 (равенство ложно), выполняется следующий оператор (строка 90). В строке 90 также находится оператор if-else. В условии этого оператора if-else (в скобках) проверяется текущее значение переменной key__released_counter, и ес- ли оно не равно 0 (истина), то выполняется операция декремента (знак операции — —), т. е. значение переменной key_released_counter уменьшается на 1 (см. Унарные операции), и выполнение программы переходит на строку 91. Если значение переменной key_released__counter равно 0 (ложь), то выполнение про- граммы сразу переходит на строку 91 (декремен не выполняется). В строке 91 находится ключевое слово else оператора if-else, означающее нача- ло группы операторов (составной оператор), которые будут выполняться, если ус- ловие оператора if-else (строка 90) ложно. Составной оператор заключен в фигур- ные скобки, находящиеся в строках 92 (открывающая скобка) и 109 (закрываю- щая скобка). В строке 93 в условии очередного оператора if-else проверяется выражение, стоящее в скобках. Это выражение вычисляется в следующем порядке. Сначала значение переменной key_pressed_counter декрементируется (знак операции —), т. е. уменьшается на 1. Затем полученное значение сравнивается с числом 9. Если они равны, выражение истинно, если нет — ложно. Если выражение истинно, то переменной crt__key присваивается текущее значение переменной row_data, и вы- полнение программы переходит на строку 109 (точнее, 111, т. к. в строках 109, 110 нет исполняемых операторов). Если выражение, стоящее в скобках, ложно, то выполнение программы переходит на строку 94. В строке 94 находится ключевое слово else оператора if-else, означающее нача- ло группы операторов (составной оператор), которые будут выполняться, если ус- ловие оператора if-else (строка 93) ложно. Составной оператор заключён в фигур- ные скобки, находящиеся в строках 95 (открывающая скобка) и 108 (закрываю- щая скобка). В строке 96 в условии оператора if-else проверяется выражение, стоящее в скобках. Это выражение будет истинно, если значение переменной row_data НЕ РАВНО значению переменной crt__key (знак операции !=), в противном слу- чае выражение ложно. Если выражение истинно, то выполняется группа операто- ров (составной оператор), заключённая в фигурные скобки, находящиеся в стро- ках 97 (открывающая скобка) и 102 (закрывающая скобка). Если выражение, сто- ящее в скобках, ложно, то выполнение программы переходит на строку 103. В строке 98 находится метка new__key. Она используются только для указания места, куда передаётся управление оператором goto, находящимся в строке 89. В строке 99 переменной keypressedcounter присваивается значение 10. В строке 100 переменной key_released_counter присваивается значение 0. В строке 101 оператор goto (см. Оператор goto) осуществляет переход выпол- нения программы на метку end_key (на строку 110). В строке 103 в условии оператора if-else проверяется выражение, стоящее в скобках. Это выражение будет истинно (не ноль), если значение переменной key__pressed__counter равно 0, т. к. операция логического отрицания НЕ (знак опе- рации !) вырабатывает значение 0, если операнд есть истина, и значение 1, если
7.8. Проект «Keypad» 499 операнд есть ложь (ноль). В противном случае выражение ложно. Выражение в скобках (!key_pressed__counter) в данном случае можно заменить другим: (key_pressed_counter == 0). Если выражение в скобках истинно, то выполняется группа операторов (со- ставной оператор), заключённая в фигурные скобки, находящиеся в строках 104 (открывающая скобка) и 107 (закрывающая скобка). Если выражение, стоящее в скобках, ложно, то выполнение программы переходит на строку 108 (точнее, 111, т. к. в строках 108... 110 нет исполняемых операторов). В строке 105 глобальной переменной keys присваивается текущее значение локальной переменной row_data. В строке 106 переменной key_released_counter присваивается значение 20. В строке 110 находится метка end__key. Она используются только для указания места, куда передаётся управление оператором goto, находящимся в строке 101. В строке 111 переменной row__data присваивается значение 0. В строке 115 перед компиляцией препроцессор заменит идентификатор KEYOUT на PORTD (см. строку 57). Таким образом, в этом выражении сначала берётся значение переменной column, в котором все 0 заменяются на 1, а 1 — на 0 (операция побитового логического отрицания, знак операции ~), а затем полу- ченный результат записывается в регистр PORTx Порта D (см. Доступ к регист- рам ввода/вывода). При этом значении переменной column НЕ ИЗМЕНЯЕТСЯ. На этом выполнение программы (функции) обслуживания прерывания по пе- реполнению таймера/счётчика 0 заканчивается. Функция timer0_int осуществляет опрос клавиатуры. Каждый вывод младшего ниббла Порта D (PD0...PD3) сконфигурирован как вход (см. ниже функцию init_keypad, строка 128) и подключён к соответствующему ряду клавиатуры (см. Рис. 7.14). Каждый вывод старшего ниббла Порта D (PD4...PD7) сконфигуриро- ван как выход и через диод (VD1...VD4) подключён к соответствующему столбцу клавиатуры. Первоначально на всех четырёх выводах младшего ниббла Порта D (PD0...PD3) присутствует уровень логической 1 благодаря подтягивающим ре- зисторам R1...R4. Алгоритм опроса следующий. На каждый вывод старшего ниббла Порта D (PD4...PD7) последовательно подаётся уровень логического 0, считывается зна- чение младшего ниббла Порта D и записывается в младший ниббл переменной row_data, причём предыдущее значение переменной row_data предварительно сдвигается на один ниббл влево. Если в текущем столбце (на который подан 0) есть нажатая кнопка, то на входах в бите, который соответствует ряду с нажатой кнопкой, также появится 0. Таким образом, в переменной row_data получается код нажатой кнопки. Алгоритм работы функции timerOJnt представлен блок-схемой на Рис. 7.16.
500 Глава 7. Примеры проектов Рис. 7.16. Блок-схема функции timerOJnt. Блок 1 выполняется только один раз при самом первом вызове функции timerOJnt, т. к. все локальные переменные этой функции объявлены с классом па- мяти static. При последующих вызовах функции timerOJnt блок 1 игнорируется. Все остальные блоки функции timerOJnt выполняются (в соответствии с алго- ритмом) при каждом вызове этой функции.
7.8. Проект «Keypad» 501 В блоке 2 происходит новая инициализация таймера/счётчика 0, т. е. начина- ется новый отсчёт таймера/счётчика 0 для инициирования следующего прерыва- ния (см. строку 79 программы) по его переполнению. В блоке 3 происходит сдвиг с присваиванием значения переменной row_data на 1 ниббл (4 бита) влево (см. строку 80 программы). В блоке 4 считывается текущее значение регистра PIND; в этом значении все 1 заменяются на 0, а 0 — на 1, и полученное значение заносится в младший ниббл переменной row_data (см. строку 83 программы). В блоке 5 происходит сдвиг с присваиванием значения переменной column на 1 бит вправо (см. строку 84 программы). Если полученное значение не равно вы- ражению LAST__COLUMN»1 (блок 6), то все 1 в значении переменной column за- меняются на 0, а 0 — на 1, и полученное значение записывается (присваивается) в регистр PORTD (блок 18), и выполнение функции timerO__int заканчивается. После четырёх вызовов функции timerO_int клавиатура будет полностью опро- шена, а в переменной row_data будет находиться код нажатой кнопки клавиатуры. Если ни одна кнопка не нажата, то в переменной row data будет значение 0Ь 0000 0000 0000 0000. Текущее значение переменной row data присваивается переменной crt__key. Затем полный цикл опроса клавиатуры для определения нажатой кнопки (че- тырёхкратное повторение цепочки 2->3->4->5->6->18) повторяется 10 раз (10 — значение переменной key_pressed_counter в блоке 14). После каждого раза срав- ниваются значения переменных row__data и crt__key (блок 13) и, если все 10 раз они были равными, то значение переменной row_data присваивается переменной keys (блок 16). Если значения переменных row_data и crt__key хоть раз оказались не равны, то полный цикл опроса клавиатуры для определения нажатой кнопки снова повторяется 10 раз (переменной key_pressed_counter в блоке 14 снова при- сваивается значение 10). Таким образом, уменьшаются ошибки, связанные с дре- безгом контактов, нечетким нажатием кнопки клавиатуры и т. д. После того как значение переменной row_data присвоено переменной keys, т. е. определена нажатая кнопка клавиатуры, начинается опрос клавиатуры для определения момента отпускания кнопки. Снова осуществляется полный цикл опроса клавиатуры (четырёхкратное повторение цепочки 2->3^4^5->6->18). Количество полных циклов задаётся значением переменной key_released_counter в блоке 16. После каждого цикла опроса проверяется условие равенства значения переменной row_data нулю (блок 16), что является критерием того, что все кноп- ки отжаты (нет нажатых кнопок). Если условие выполняется, то начинается но- вый цикл опроса клавиатуры для определения нажатой кнопки. Если нет, то оп- рос клавиатуры для определения момента отпускания кнопки повторяется. На этом закончим описание функции timer0_int и вернёмся к описанию ос- тальной части программы, находящейся в файле keypad.c. Строки 119... 124 — это определение функции inkey. В строке 119 осуществляется объявление функции inkey. Определитель unsigned (подразумевается unsigned int (беззнаковое целое), см. Типы данных) задаёт тип зна- чения, которое возвращает функция. Далее следует имя функции inkey (может быть произвольным). По этому имени далее в программе осуществляется вызов данной
502 Глава 7. Примеры проектов функции. Ключевое слово void в скобках указывает на то, что в эту функцию не пе- редаются никакие параметры (см. Функции). В строке 120 открывающая фигурная скобка указывает на начало тела функ- ции inkey. В строке 121 объявляется локальная переменная к типа unsigned int (беззнако- вое целое). Ключевое слово unsigned в скобках означает unsigned int (см. Типы дан- ных). В строке 122 находится укороченный оператор if-else (без else). Так как в его теле всего один оператор, вся конструкция записана в одну строку, без фигурных скобок. В условии оператора if-else (в скобках) локальной переменной к присваи- вается значение глобальной переменной keys, и если оно не равно 0 (истинно), то глобальной переменной keys присваивается значение 0 и выполняется следую- щий оператор (строка 123). Если значение переменной к после присваивания равно 0 (ложно), то СРАЗУ выполняется следующий оператор (строка 123). В строке 123 находится оператор return, который завершает выполнение функ- ции, в которой он задан, и возвращает управление в вызывающую функцию. Зна- чение выражения, стоящего за оператором return, возвращается в вызывающую функцию в качестве значения вызываемой функции (см. Оператор return). В строке 124 закрывающая фигурная скобка указывает конец тела функции inkey. Таким образом, функция inkey возвращает значение локальной переменной к, которой присвоено значение глобальной переменной keys. Значение глобальной переменной keys после выполнения функции inkey в любом случае равно 0. Строки 126... 133 — это определение функции init_keypad. Эта функция произ- водит инициализацию периферийных устройств микроконтроллера. В строке 126 осуществляется объявление функции init_keypad. В начале стоит ключевое слово void, указывающее на то, что эта функция не возвращает никаких значений. Далее следует имя функции init_keypad (может быть произвольным). По этому имени далее в программе осуществляется вызов данной функции. Клю- чевое слово void в скобках указывает на то, что в эту функцию не передаются ни- какие параметры (см. Функции). В строке 127 открывающая фигурная скобка указывает начало тела функции init_keypad. В строке 128 в регистр DDRx Порта D записывается число ОхГО (0xf0=0b 1111 0000), тем самым все выводы старшего ниббла этого порта (биты PD4...PD7) де- лаются выходами, а все выводы младшего ниббла (биты PD0...PD3) — входами. Подробнее о регистре DDRx (см. Доступ к регистрам ввода/вывода). В строке 129 вместо, идентификатора INIT_TIMER0 препроцессор компиля- тора подставит выражение, которое определено в строке 59. Таким образом, в ре- гистр TCNT0 таймера/счётчика 0 будет записано начальное значение, рассчитан- ное по соответствующей формуле (см. описание строки 79). Именно с этого зна- чения будет начинаться счёт таймера/счётчика 0. В строке 130 в регистр управления таймером/счётчиком 0 TCCR0 записывает- ся число 3, тем самым коэффициент деления предварительного делителя частоты таймера/счётчика 0 устанавливается равным 64, т. е. таймер/счётчик 0 будет так- тироваться частотой в 64 раза меньше системной (частоты кварца).
7.8. Проект «Keypad» 503 При таких параметрах прерывания по переполнению таймера/счётчика 0 бу- дут происходить примерно каждые 2 мс. В строке 131 в регистр маски прерывания от таймеров/счётчиков TIMSK за- писывается число 2, тем самым разрешается прерывание по переполнению тай- мера/счётчика 0 (если установлено глобальное разрешение прерываний). Подробнее о соответствующих регистрах микроконтроллера см. описание (datasheet) от Atmel на микроконтроллер AT90S8515 (см. Команда Help -> AVR Data Sheets (Помощь -> Описания AVR)). В строке 132 осуществляется глобальное разрешение прерываний. Для этого в программу включён ассемблерный код (см. Директивы #asm и ttendasm). Инс- трукция ассемблера sei устанавливает флаг глобального прерывания I в регистре статуса SREG микроконтроллера, тем самым разрешая глобальные прерывания. Подробнее об инструкциях ассемблера см. систему помощи бесплатной програм- мы AVR Studio от Atmel, которую можно скачать на сайте разработчиков микро- контроллеров AVR http://www.atmel.com. В строке 133 закрывающая фигурная скобка указывает на конец тела функции init_keypad. Строки 135... 154 — это определение основной функции программы (main). Эта функция обязательно должна присутствовать во всех программах (но только ОДНА!). Именно с этой функции, в каком бы месте программы она ни находи- лась, начинается выполнение программы. В строке 135 осуществляется объявление функции main. В начале опущено (но подразумевается) ключевое слово void, указывающее на то, что эта функция не возвращает никаких значений; затем следует имя функции main (ЕГО ИЗМЕ- НЯТЬ НЕЛЬЗЯ!). В скобках опущено (но подразумевается) ключевое слово void, которое указывает на то, что в эту функцию не передаются никакие параметры (см. Функции). В строке 136 открывающая фигурная скобка указывает на начало тела функ- ции main. В строке 137 объявляется локальная переменная к типа unsigned int (беззнако- вое целое). Ключевое слово unsigned в скобках означает unsigned int (см. Типы дан- ных). Не следует путать её с локальной переменной к, объявленной в функции inkey. Несмотря на одно имя, эти переменные РАЗНЫЕ . Их видно только в тех функциях, где они объявлены (см. Переменные). В строке 138 осуществляется вызов функции init_keypad, которая определена в строках 126... 133. Эта функция инициализирует периферийные устройства мик- роконтроллера. В строке 139 осуществляется вызов функции lcd_init с фактическим парамет- ром 16 (количество столбцов в используемом модуле LCD). Эта функция инициа- лизирует модуль LCD, очищает дисплей и устанавливает позицию для вывода символа в ряд 0 столбца 0. Курсор не отображается. В строке 140 осуществляется вызов функции lcd_putsf, которая отображает в текущей дисплейной позиции строку «CVAVR Keypad». Обе эти функции определены в подключённом файле led.h (строка 47). Под- робнее см. LCD-функции для дисплеев до 2x40 символов.
504 Глава 7. Примеры проектов В строках 143...153 реализован бесконечный цикл с помощью оператора while (см. Оператор while). В строке 143 — начало этого оператора, на что указывает ключевое слово while. Пока условие этого оператора (в скобках) истинно, т. е. значение выраже- ния отлично от нуля, будет выполняться группа операторов (составной оператор, тело оператора while), заключённая в фигурные скобки, находящиеся в строках 144 (открывающая скобка) и 153 (закрывающая скобка). Выражение в скобках — 1 (можно также написать 55 или 16), а не 0, т. е. всегда истинно. Следовательно, эта группа операторов будет выполняться бесконечно, пока не произойдёт пре- рывание программы. В строке 145 осуществляется вызов функции lcd_gotoxy с фактическими пара- метрами (0, 1). Эта функция устанавливает текущую позицию дисплея соответс- твенно в столбец 0 и ряд 1. Нумерация рядов и столбцов начинается с 0. Эта фун- кция определена в подключённом файле led.h (строка 47). Подробнее см. LCD-функции для дисплеев до 2x40 символов. В строках 146...152 находится оператор if-else (см. Оператор if-else). В строке 146 — начало этого оператора, на что указывает ключевое слово if. Порядок вычисления выражения в скобках (условие оператора if-else) следую- щий. Сначала вызывается функция inkey, которая определена в строках 119...124. Значение, которое возвращает функция inkey, присваивается локальной перемен- ной к. Если это значение равно 0, то выражение в скобках будет ложно. Во всех остальных случаях выражение будет истинно. Если выражение в скобках истинно, то будет выполняться группа операторов (составной оператор), заключённая в фигурные скобки, находящиеся в строках 147 (открывающая скобка) и 150 (закрывающая скобка). После этого выполнение программы будет продолжено со строки 152. Если выражение в скобках ложно, то будет выполняться оператор, находящийся в строке 151. В строке 148 функция sprintf осуществляет форматные преобразования и по- мещает результаты в строку buf. Спецификация преобразования «Key code = %Xh» означает, что сначала будет выведена последовательность «Key code =», затем зна- чение локальной переменной к в шестнадцатеричном виде символами верхнего регистра (символ преобразования 'X'). Затем будет выведен символ «Ь». Эта функ- ция определена в подключённом файле stdio.h (строка 48) (см. Стандартные фун- кции ввода/вывода). В строке 149 функция lcd_puts отображает в текущей дисплейной позиции содер- жимое строки buf, которая была сформирована в предыдущей строке программы. В строке 151 находится ключевое слово else оператора if-else, означающее, что эта строка будет выполняться, если условие оператора if-else (строка 146) ложно. В этой строке осуществляется вызов функции lcd_putsf, которая отображает в те- кущей дисплейной позиции последовательность «NO KEY». Функции lcd_puts и lcd_putsf определены в подключённом файле led.h (строка 47). Подробнее об этих функциях см. LCD-функции для дисплеев до 2x40 символов. В строке 152 функция delay_ms генерирует задержку 500 мс (см. Функции задерж- ки). Эта функция определена в подключённом файле delay.h (строка 49). В строке 154 закрывающая фигурная скобка указывает на конец тела функции main.
7.8. Проект «Keypad» 505 Программа работает следующим образом. После подачи питания (или аппа- ратного сброса на выводе RESET микроконтроллера) начинает выполняться функция main. Последовательно вызываются функция init_keypad, которая ини- циализирует Порт D, таймер/счётчик 0 и настраивает систему прерываний, функ- ция lcd_init, которая инициализирует LCD-модуль, и функция led_putsf, которая в верхнюю строку LCD-модуля выводит последовательность «CVAVR Keypad». После этого программа переходит в бесконечный цикл, в котором сначала курсор LCD устанавливается в начальную позицию нижней строки, затем вызы- вается функция inkey, которая возвращает код нажатых кнопок клавиатуры, и этот код отображается на LCD после надписи «Key code=». В конце выводится символ «И», показывающий, что код выводится в шестнадцатеричном виде. Если ни одна кнопка не нажата, то выводится последовательность «NO KEY» (см. Рис. 7.17). После этого генерируется временная пауза в 500 мс, и после неё бесконечный цикл повторяется снова. Во время выполнения функции main (в том числе и во время паузы в 500 мс) постоянно, через каждые 2 мс, происходит прерывание по переполнению таймера/счётчика 0 и вызывается функция timerOJnt, которая производит опрос клавиатуры и в зависимости от нажатых кнопок клавиатуры формирует соответствующий код. Код нажатых кнопок следует интерпретировать следующим образом. Млад- ший (первый) бит кода соответствует столбцу 1, старший (четвертый) — столбцу 4. Если в столбце нажата кнопка в ряду А, то в этом бите будет число 1, если в ряду В — число 2, С — 4, D — 8. При нажатии в одном столбце двух и более кнопок будет выведена сумма их кодов. Пример: Нажата кнопка «6». Эта кнопка находится в столбце 3, ряд В. Следовательно, в третьем бите будет число 2, т. е. код нажатых кнопок будет: 0200. Таким образом, на LCD будет выведен код 200h, т. к. незначащий 0 впереди, согласно формату вывода (см. строку 148 программы), будет опущен (Рис. 7.17). Рис. 7.17. Результат работы программы при нажатой кнопке «6». R1 ...R4 = 10 k...47 к VD1...VD4= 1N4148 (КД521, КД522)
506 Глава 7. Примеры проектов Пример: Одновременно нажаты кнопки «11» и «15». Кнопка «11» — столбец 4, ряд С; кнопка «15» — столбец 4, ряд D. Следовательно, в самом старшем (четвертом) би- те (столбец 4) будет число С (ряд С — 4, ряд D — 8; 4 + 8 = 12 = ОхС), т. е. код на- жатых кнопок будет: С000. Таким образом, на LCD будет выведен код COOOh (Рис. 7.18). Рис. 7.18. Результат работы программы при одновременно нажатых кнопках «11» и «15». R1 ...R4 = 10 k...47 к VD1...VD4= 1N4148 (КД521, КД522) Обратите внимание, что в строке 59 значение вычитаемого в выражении полу- чается равным 3686400/64/500 = 115.2. Дробная часть будет отброшена, и в регистр TCNT0 будет записано значение 256 - 115 = 141 (напомним, что 0x100 = 256). Та- ким образом, прерывание по переполнению таймера/счётчика 0 будет происходить не через 2 мс, а через 64х(256 — 141)/3686400 » 0.001997 с = 1.997 мс. В данном при- мере это не важно, но в других приложениях это может оказаться существенным. Чтобы пауза была точной, следует подобрать частоту кварца, коэффициент деления предварительного делителя частоты таймера/счётчика 0 и начальное значение ре- гистра TCNT0. Например, в данном случае можно взять частоту кварца 4 МГц, ко- эффициент деления 64. Тогда начальное значение регистра TCNT0 (см. строку 59 программы) получится равным 256 — 4000000/64/500 = 131. Тогда время между прерываниями получится: 64х(256 — 131)/4000000 = 0.002 с = 2 мс. Таким образом, для получения точной паузы в 2 мс следует изменить строку 53 программы: 52: // частота кварца [Гц] 53: «define F_XTAL 4000000L Это наглядно демонстрирует удобство применения директивы #define — не требуется изменять остальной текст программы. Разумеется, что в этом случае следует заменить кварц при аппаратной реализации проекта. Не забудьте также изменить значение тактовой частоты в настройках проекта (Рис. 7.19).
7.8. Проект «Keypad» 507 Рис. 7.19. Изменение значение тактовой частоты в настройках проекта. Обратите внимание, что при первом вызове функции timerO_int в регистр PORTx Порта D будет записано значение 0Ь 0000 0000 (по умолчанию), а не 0Ь 0111 1111, как того требует алгоритм опроса клавиатуры. Это может привести к неверному результату. И хотя в программе приняты меры для исключения ошиб- ки (десятикратный опрос), рекомендуется этого не допускать. Для устранения этой неточности в функции инициализации init_keypad после 128-й строки доста- точно добавить строку: KEYOUT = ~FIRST_COLUMN; То есть при инициализации в регистр PORTD будет записано значение 0Ь 0111 1111. При аппаратной реализации данного проекта было замечено, что если ка- кую-либо кнопку клавиатуры удерживать нажатой, то наряду с кодом нажатой кнопки периодически выводится строка «NO KEY». Это происходит из-за того, что значение переменной keys независимо от нажатых кнопок обнуляется в функ- ции inkey, которая периодически вызывается через каждые 500 мс. Кроме того, можно обойтись без отдельного цикла опроса клавиатуры для определения мо- мента отпускания кнопки. Логичнее продолжать опрос клавиатуры для определе- ния кода нажатой кнопки, т. к. об отпускании кнопки можно судить по нулевому значению этого кода (значение переменной keys).
508 Глава 7. Примеры проектов Для устранения указанных недостатков и для упрощения программы доста- точно изменить алгоритм работы функции timerO_int, как показано на блок-схе- ме, представленной на Рис. 7.20. Рис. 7.20. Обновлённая блок-схема функции timerO_int.
7.8. Проект «Keypad» 509 Программная реализация функции timerOJnt по этому алгоритму: П‘. // прерывание по переполнению таймера/счётчика 0 каждые 2 мс 72: interrupt [TIM0_OVF] void timer0_int(void) 73: { 74: static byte key_pressed_counter = 20; 75: static byte column - FIRST-COLUMN; 76: static unsigned row_data,crt_key; 77: 78: // заново инициализируем таймер/счётчик 0 79: INIT—TIMER0; 80: row_data<<=4; 81: 82: // получим группу 4 кнопок в переменной row_data 83: row_data1 —KEYIN&Oxf; 84: column>>=l; 85: if (column== (LAST.COLUMN»1) ) 86: { 87: column=FIRST_COLUMN; 88: if (--key_pressed_counter==9) crt_key = row_data; 89: else 90: { 91: if (row_data!=crt_key) key_pressed_counter = 10; 92: else 93: { 94: if (!key_pressed_counter) 95: { 96: keys=row_data; 97: key_pressed_counter = 10; 98: }; 99: }; 100: }; 101: row_data = 0; 102: }; 103: // выберем следующий столбец, входы будут 104: //с подтягивающими резисторами 105: KEYOUT—column; 106: } При такой реализации удалось избавиться от переменной key_released_counter, что позволяет сэкономить память и уменьшить размер полу- чаемого кода. Кроме того, архаичный оператор goto был заменён частью else опе- ратора if-else. Обратите внимание, что при выводе строки «NO KEY» (строка 151) количест- во символов в строке (вместе с пробелами) — 14. Это может привести к тому, что если перед выводом этой последовательности были выведены символы в позици- ях 15 и 16 этой строки LCD, то они останутся, что исказит информацию. Чтобы этого не произошло, следует добавить пробелы так, чтобы общее количество сим- волов в строке составляло 16, или перед каждым выводом очищать дисплей LCD (см. LCD-функции).
510 Глава 7. Примеры проектов Чтобы код нажатой кнопки всегда выводился четырехзначным числом (срав- ните Рис. 7.17 и Рис. 7.18), следует изменить формат вывода в операторе sprintf (см. Стандартные функции ввода/вывода) функции main: 148: sprintf(buf,"Key code=%04Xh", k); Кроме того, при таком формате вывода, чтобы поддерживалась специфика- ция ширины (04), в настройках проекта в опции (s)printf features ((s)printf харак- теристики) следует выбрать характеристику int, width (Рис. 7.21) (см. Закладка С Compiler (Компилятор Си)). Рис. 7.21. Изменение опции (s)printf features ((s)printf характеристики) в настройках проекта. С учётом всего вышесказанного программу в данном примере можно реали- зовать следующим образом: 1: /* 2 : Демонстрируется клавиатура 4x4 3: 4: CodeVisionAVR С Compiler 5: 6: Чип: AT90S8515 7: */ 8: 9: /* Перед подключением файла led.h объявим порт, используемый 10: для подключения модуля LCD */
7.8. Проект «Keypad» 511 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: #asm .equ ___lcd_port=0xl5 tfendasm /* Подключим дополнительные файлы */ #include <lcd.h> #include <stdio.h> #include <delay.h> ^include <90s8515.h> // частота кварца [Гц] tfdefine F_XTAL 4000000L // PIND0...3 будут входами рядов tfdefine KEYIN PIND // PORTD4...7 будут выходами столбцов #define KEYOUT PORTD // используем для инициализации счёта таймера/счётчика 0 tfdefine INIT_TIMER0 TCNTl=0xl00L-F_XTAL/64L/500L tfdefine FIRST-COLUMN 0x80 ^define LAST-COLUMN 0x10 typedef unsigned char byte; // сохраним здесь состояние каждой кнопки как бит, // бит 0 будет KEY0, бит 1 - KEY1,... unsigned keys; // буфер LCD-дисплея char buf[33]; // прерывание по переполнению таймера/счётчика 0 каждые 2 мс interrupt [TIMO-OVF] void timer0_int(void) { static byte key_pressed_counter=20; static byte column=FIRST-COLUMN; static unsigned row_data,crt_key; // заново инициализируем таймер/счётчик 0 INIT.TIMERO; row_data<<=4; // получим группу 4 кнопок в переменной row_data row_data I=~KEYIN&0xf; column>>=l; if (column—(LAST_COLUMN»1) ) { column=FIRST—COLUMN; if (--key_pressed-Counter—9) crt_key=row_data;
512 Глава 7. Примеры проектов 61: else 62: { 63: if (row_data!=crt_key) key_pressed_< 64: else 65: { 66: if (!key_pressed_counter) 67: { 68: keys=row_data; 69: key_pressed_counter=10; 70: }; 71: }; 72: }; 73: row_data=0; 74: }; 75: // выберем следующий столбец, входы будут 76: //с подтягивающими резисторами 77: KEYOUT=-column; 78: } 79: 80: void init_keypad(void) 81: 82: DDRD = OxfO; 83: KEYOUT = -FIRST_COLUMN; 84: INIT.TIMERO; 85: TCCR0 = 3; 86: TIMSK = 2; 87: #asm("sei") 88: } 89: 90: main() 91: { 92: unsigned k; 93: init_keypad() ; 94: lcd_init(16) ; 95: lcd_putsf("CVAVR Keypad"); 96: 97: // прочитаем кнопку и отобразим её код 98: while (1) 99: { 100: lcd_gotoxy(0,1); 101: if (keys) 102: { 103: sprintf(buf,"Key code=% 04Xh",keys); 104: lcd_puts(buf); 105: } 106: else lcd_putsf("NO KEY "); 107: delay_ms(500); 108: } 109: } counter=10;
7.9. Проект «Dsl820» 513 Результат работы откорректированной программы при нажатой кнопке «6» показан на Рис. 7.22. (сравните с Рис. 7.17). Рис. 7.22. Результат работы откорректированной программы при нажатой кнопке «6». R1...R4 = 10k...47k VD1...VD4- 1N4148 (КД521, КД522) Следует отметить, что в данном примере не совсем корректно используется функция задержки. Как уже отмечалось ранее, для правильной работы этих функций до их вызова должны быть запрещены прерывания (см. Функции за- держки). В данном примере этого сделать нельзя, т. к. иначе в период временной задержки не будет происходить опрос клавиатуры. Действительная задержка бу- дет значительно дольше 500 мс. 7.9. Проект «Ds1820» Этот пример демонстрирует многоточечный термометр, реализованный на температурных датчиках DS1820/18S20 с шиной 1-Wire от Dallas Semiconductor. Информация выводится на алфавитно-цифровой LCD-модуль 2x16. В этом проекте содержится один исходный файл — dsl820.c. Текст этого фай- ла с русифицированными комментариями: 2: Многоточечный термометр с LCD-дисплеем 3: используются температурные датчики DS1820/18S20 4: с шиной 1-Wire от Dallas Semiconductor э : б: CodeVisionAVR С Compiler 7: (С) 2000-2002 HP InfoTech S.R.L. 8: 9: www.hpinfotech.ro 10: Чип: AT90S8515 11: Модель памяти: SMALL 12: Размер стека данных: 128 Б 13:
514 Глава 7. Примеры проектов 14: ТАКТОВАЯ ЧАСТОТА AT90S8515 ДОЛЖНА БЫТЬ 3.6864 МГц 15: Датчики DS1820/18S20 подключены к 16: биту 6 ПОРТА A AT90S8515 следующим образом: 17: 18: [DS1820/18S20] [Разъём PORTA STK500] 19: 1 GND - 9 GND 20: 2 DQ - 7 PA6 21: 3 VDD - 10 +5 в 22: 23: 24: Все температурные датчики должны быть подключены параллельно 25: 26: МЕЖДУ DQ (РАб) И +5 В ДОЛЖЕН БЫТЬ подключён 27: ПОДТЯГИВАЮЩИЙ РЕЗИСТОР 4.7 к! 28: */ 29: 30: #asm 31: .equ wl_port=0xlb 32: .equ wl_bit=6 33: #endasm 34: 35: /* 36: Используйте алфавитно-цифровой LCD 2x16, подключённый 37: к PORTC следующим образом: 38: [LCD] [STK500 PORTC HEADER] 39: 1 GND - 9 GND 40: 2 +5V - 10 VCC 41: 3 VLC - напряжение управления контрастом LCD 0...1 В 42: 4 RS - 1 PC0 43: 5 RD - 2 PCI 44: 6 EN - 3 PC2 45: 11 D4 - 5 PC4 46: 12 D5 - 6 PC5 47: 13 D6 - 7 PC6 48: 14 D7 - 8 PC7 49: */ 50: 51: #asm 52: .equ lcd_port=0xl5 53: #endasm 54: 55: #include <lcd.h> // программы управления LCD 56: ttinclude <dsl820.h> 57: ttinclude <delay.h> 58: tfinclude <math.h> 59: ttinclude <stdio.h> 60: 61: char lcd_buffer[33]; 62: 63: /* максимальное количество DS1820/DS18S20,
7.9. Проект «Dsl820> 515 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: подключённых к шине 1-Wire */ #define MAX-DEVICES 8 /* область хранения ROM-кода устройств DS1820/DS18S20 */ unsigned char rom_code[MAX-DEVICES,9]; main() { unsigned char i,j,devices; int temp; lcd_init(16); lcd_putsf("CodeVisionAVR\nl Wire Bus Demo"); delay_ms(2000); lcd_clear(); /* определим, сколько устройств DS1820/DS18S20 подключено к шине 1-Wire */ devices=wl_search(Oxf0,rom_code); sprintf(lcd_buffer,"%u DS1820\nDevice detected",devices); lcd_puts(lcd_buffer); delay_ms(2000); /* отобразим ROM-код каждого устройства */ if (devices) { for (i=0;i<devices;i++) { sprintf(lcd_buffer,"Device #%u ROM\nCode is:",i+l); lcd_clear(); lcd_puts(lcd_buffer); delay_ms(2000); lcd_clear(); for (j=0;j<8;j++) { sprintf(lcd_buffer,"%02X ",rom_code[i,j]); lcd_puts(lcd_buffer); if (j==3) lcd_gotoxy(0,1); }; delay_ms(5000); }; } else while (1); /* здесь остановка, если не было обнаружено никакого устройства */ /* измерим и отобразим температуры */ while (1) { for (i=0;i<devices;)
516 Глава 7. Примеры проектов 114: { 115 : temp=dsl820_temperature_10(&rom_code[i,0]); 116 : sprintf(lcd_buffer,"t%u=%i.%u\xdfC",++i,temp/10,abs(temp%10)); 117: lcd_clear(); 118: lcd_puts(lcd_buffer); 119: delay_ms(800); 120: }; 121: }; 122: } В этом файле знаками /* (начало) и */ (конец) выделены блоки с коммента- риями, а знаком // — строки с комментариями (см. Комментарий). В блоке с комментариями (строки 1...28) даются рекомендации по подключе- нию температурных датчиков к отладочной плате STK500. В строках 30...33 объявляется, какой порт микроконтроллера и какой бит пор- та будут использованы для связи через протокол 1-Wire. Для этого в программу включён ассемблерный код. В строке 30 директива #asm говорит компилятору о начале ассемблерного кода, а в строке 33 директива #endasm — о его завершении (см. Директивы #asm и ttendasm). В строке 31 директива ассемблера .equ присваи- вает значение идентификатору___wl_port. Это значение, соответствующее адресу регистра PORTx выбранного порта (в данном случае выбран Порт А), находим в файле 90s8515.h в соответствующей строке: «sfrb PORTA=0xlb;». Этот файл нахо- дится в поддиректории ..\INC. В строке 32 директива ассемблера .equ присваивает значение идентификатору __wl_bit. Это значение соответствует номеру соответствующего бита. В следующем блоке с комментариями (строки 35...49) даются рекомендации по подключению к отладочной плате STK500 алфавитно-цифрового LCD-модуля 2x16. В строках 51...53 объявляется, какой порт микроконтроллера будет использо- ваться для подключения LCD-модуля. Для этого в программу включён ещё один ассемблерный код. В строке 51 директива #asm говорит компилятору о начале ас- семблерного кода, а в строке 53 директива #endasm — о его завершении (см. Ди- рективы #asm и ttendasm). В строке 52 директива ассемблера .equ присваивает зна- чение идентификатору____lcd_port. Это значение, соответствующее адресу регист- ра PORTx выбранного порта (в данном случае выбран Порт С), находим в файле 90s8515.h в соответствующей строке: «sfrb PORTC=Oxl5;». При трансляции ассемблерного кода, полученного при компиляции данного проекта компилятором Си CodeVisionAVR, ассемблер вместо идентификаторов __wl_port, __wl_bit и___lcd_port подставит их значения. В случае модификации программы при использовании другого порта и/или бита порта для связи через протокол 1-Wire, а также другого порта для подключе- ния LCD-модуля, достаточно будет лишь заменить значения в указанных дирек- тивах .equ, не изменяя остального текста программы. Подробнее о директивах ассемблера см. систему помощи бесплатной про- граммы AYR Studio от Atmel, которую можно скачать на сайте разработчиков мик- роконтроллеров AVR: http://www.atmel.com.
7.9. Проект «Dsl820> 517 Директивами #include (строки 55...59) подключаются: LCD-функции — файл led.h (см. LCD-функции для дисплеев до 2x40 символов)', функции DS 1820/DS18S20 — файл dsl820.h (см. Функции температурного датчика DS1820/DS18S20 от Dallas Semiconductor)', функции задержки — файл delay.h (см. Функции задержки)', математические функции — файл math.h (см. Математические функции) и функ- ции ввода/вывода — файл stdio.h (см. Стандартные функции ввода/вывода). Пе- ред компиляцией препроцессор компилятора вставит вместо этих строк текст со- ответствующих файлов (см. Директива ^include). В строке 61 объявляется глобальный символьный массив (строковая перемен- ная) ledjbuffer, состоящий из 33 членов. Этот массив будет расположен в SRAM микроконтроллера. При этом все элементы этого массива автоматически иници- ализируются со значением 0 (см. Массивы). В этом массиве будет храниться ин- формация, предназначенная для вывода на LCD. Так как в настройках проекта на закладке С Compiler (Компилятор Си) выбра- на опция char is unsigned (байт является беззнаковым) (Рис. 7.23), то все данные типа char (байт) компилятор будет обрабатывать как данные типа unsigned char (беззнаковый байт) (см. Закладка С Compiler (Компилятор Си)), т. е. массив ledbuffer будет состоять из данных типа unsigned char (беззнаковый байт). Рис. 7.23. Опция char is unsigned (байт является беззнаковым) выбрана.
518 Глава 7. Примеры проектов Настоятельно рекомендуется явно определять тип данных в тексте про- граммы, т. к. при распечатке текста программы настроек проекта не видно. Это может вызвать неправильное понимание работы программы! Директивой #define (строка 65) определяется идентификатор MAX_DEVICES. После этой строки перед компиляцией препроцессор заменит в тексте програм- мы идентификатор MAX_DEVICES на 8. В случае модификации программы для другого значения максимального количества устройств DS1820/DS18S20, под- ключённых к шине 1 -Wire, достаточно будет лишь заменить значение в указанной директиве #define, не изменяя остального текста программы (см. Директива ttdefine, #undef). Директива #define очень удобна, но вовсе не обязательна. В строке 68 объявляется глобальный двухмерный массив rom_code, в котором будут храниться ROM-коды всех температурных датчиков. Для каждого устройс- тва используется 9 байтов (смотри описание функции wl_search в главе Функции протокола 1-Wire), но только первые 8 байтов содержат ROM-код и CRC (конт- рольную сумму). Строки 70... 122 — это определение основной функции программы (main). Эта функция обязательно должна присутствовать во всех программах (но только ОД- НА!). Именно с этой функции, в каком бы месте программы она ни находилась, начинается выполнение программы. В строке 70 осуществляется объявление функции main. В начале опущено (но подразумевается) ключевое слово void, указывающее на то, что эта функция не возвращает никаких значений; затем следует имя функции main (ЕГО ИЗМЕ- НЯТЬ НЕЛЬЗЯ!). В скобках опущено (но подразумевается) ключевое слово void, которое указывает на то, что в эту функцию не передаются никакие параметры (см. Функции). В строке 71 открывающая фигурная скобка указывает на начало тела функции main. В строке 72 объявляются переменные i, j, devices типа unsigned char (беззнако- вый байт). В строке 73 объявляется переменная temp типа int (целое) (см. Типы данных). В строке 75 осуществляется вызов функции lcd_init с фактическим парамет- ром 16 (количество столбцов в используемом модуле LCD). Эта функция инициа- лизирует модуль LCD, очищает дисплей и устанавливает позицию для вывода символа в ряд 0 столбца 0. Курсор не отображается. В строке 76 осуществляется вызов функции lcd_putsf, которая отображает в те- кущей позиции дисплея последовательность «CodeVisionAVR\nl Wire Bus Demo», т. e. сначала в верхней строке LCD будет выведена последовательность «CodeVisionAVR». Затем курсор будет переведён на нижнюю строку (последова- тельность \п), и в нижней строке будет выведена последовательность «1 Wire Bus Demo». Функции lcd_init и lcd_putsf определены в подключённом файле led.h (строка 55). В строке 77 функция delay_ms генерирует задержку 2000 мс (см. Функции за- держки). Эта функция определена в подключённом файле delay.h (строка 57).
7.9. Проект «Ds1820» 519 В строке 78 осуществляется вызов функции lcd_clear, которая очищает LCD и устанавливает позицию для вывода символа в ряд 0 столбца 0. Эта функция опре- делена в подключённом файле led.h (строка 55). Далее определяется количество устройств DS1820/DS 18S20, подключённых к шине 1-Wire (строка 82). Для этого вызывается функция wl_search с фактически- ми параметрами OxfO (команда поиска ROM-кодов) и rom_code (указатель на ту область SRAM, где хранятся ROM-коды устройств). Эта функция возвращает ко- личество устройств, подключённых к шине 1-Wire, и это значение присваивается переменной devices. Кроме того, функция wl_search заполняет массив rom_code ROM-кодами устройств DS1820/DS18S20, подключённых к шине 1-Wire (см. Функции протокола 1-Wire). В строке 83 функция sprintf осуществляет форматные преобразования и поме- щает результаты в строку lcd_buffer. Спецификация преобразования «%и DS1820\nDevice detected» означает, что сначала будет выведено значение глобаль- ной переменной devices в десятичном беззнаковом формате (символ преобразова- ния ’и’), затем через пробел — последовательность «DS1820». После этого курсор будет переведён на новую строку (последовательность \п), и в новой строке будет выведена последовательность «Device detected». Эта функция определена в под- ключённом файле stdio.h (строка 59) (см. Стандартные функции ввода/вывода). В строке 84 функция led puts отображает в текущей дисплейной позиции со- держимое строки lcd_buffer, которая была сформирована в предыдущей строке программы. Эта функция определена в подключённом файле led.h (строка 55). В строке 85 функция delay_ms снова генерирует задержку 2000 мс, как и в строке 77. В строках 88... 107 находится оператор if-else (см. Оператор if-else), с помощью которого осуществляется вывод на LCD ROM-кода каждого устройства DS1820/DS 18S20, подключённого к шине 1-Wire. В строке 88 — начало этого оператора, на что указывает ключевое слово if. Вы- ражение в скобках (условие оператора if-else) — это значение переменной devices. Оно будет истинным (не равно нулю), если функция wisearch в строке 82 обна- ружит хотя бы одно устройство. В противном случае выражение ложно (равно ну- лю). Если выражение в скобках истинно, то будет выполняться группа операторов (составной оператор), заключённая в фигурные скобки, находящиеся в строках 89 (открывающая скобка) и 105 (закрывающая скобка). После этого выполнение программы будет продолжено со строки 111. Если выражение в скобках ложно, то выполнение программы перейдёт на строку 106. В строке 90 находится оператор for (см. Оператор for). Тело этого оператора, заключённое в фигурные скобки, находящиеся в строках 91 (открывающая скоб- ка) и 104 (закрывающая скобка), будет выполняться для каждого устройства DS1820/DS 18S20, т. е. столько раз, сколько устройств подключено к шине 1-Wire. В строке 92 функция sprintf осуществляет форматные преобразования и помещает результаты в строку lcd_buffer. Спецификация преобразования «Device #%u ROM\nCode is:» означает, что сначала будет выведена последова- тельность «Device#», затем текущее значение переменной i, увеличенное на 1 (что будет соответствовать номеру устройства) в десятичном беззнаковом фор-
520 Глава 7. Примеры проектов мате (символ преобразования и’) и через пробел последовательность «ROM». После этого курсор будет переведён на новую строку (последовательность \п), и в новой строке будет выведена последовательность «Code is:». Эта функция определена в подключённом файле stdio.h (строка 59) (см. Стандартные функ- ции ввода/вывода). В строке 93 осуществляется вызов функции lcd_clear, которая очищает LCD и устанавливает позицию для вывода символа в ряд 0 столбца 0. В строке 94 функция lcd_puts отображает в текущей дисплейной позиции со- держимое строки Icdjbuffer, которая была сформирована в 92 строке программы. В строке 95 функция delay_ms генерирует задержку 2000 мс. В строке 96 осуществляется вызов функции lcd_clear, которая снова очищает LCD и устанавливает позицию для вывода символа в ряд 0 столбца 0. В строке 97 находится оператор for (см. Оператор for). Тело этого оператора, заключённое в фигурные скобки, находящиеся в строках 98 (открывающая скоб- ка) и 102 (закрывающая скобка), будет выполняться 8 раз (j = 0...7), по количеству значимых байтов ROM-кода. В строке 99 функция sprintf осуществляет форматные преобразования и поме- щает результаты в строку lcd_buffer. Спецификация преобразования «%02Х» озна- чает, что сначала будет выведено значение элемента с индексом (i, j) массива rom_code в поле шириной, по крайней мере, в два символа (описатель width = 02) в шестнадцатеричном виде символами верхнего регистра (символ преобразова- ния ’X’). После этого будет выведен пробел. Если значение имеет менее двух цифр, то оно будет выровнено по правому краю (т. к. флаг отсутствует), а левая часть будет заполнена нулями (т. к. в описателе width вначале стоит 0) (см. Стан- дартные функции ввода/вывода). В строке 100 функция lcd_puts отображает в текущей дисплейной позиции со- держимое строки Icdjbuffer, которая была сформирована в предыдущей строке программы. В строке 101 находится укороченный оператор if-else (без else). Так как в его теле всего один оператор, вся конструкция записана в одну строку, без фигурных скобок. В условии оператора if-else (в скобках) проверяется, равно ли значение переменной j числу 3. Если равно (истина), то вызывается функция lcd_gotoxy с фактическими параметрами (0,1). Эта функция устанавливает текущую позицию дисплея соответственно в столбец 0 ряда 1. Нумерация рядов и столбцов начина- ется с 0. Эта функция определена в подключённом файле led.h (строка 55). Под- робнее см. LCD-функции для дисплеев до 2x40 символов. Если] не равно 3 (ложь), то строка 101 пропускается. Таким образом, в верхнюю строку LCD через пробел будут выведены значе- ния четырёх младших байтов (0...3) ROM-кода соответствующего устройства, а во вторую строку — четырёх старших байтов (4...7). После выполнения цикла for (строка 97) в строке 103 функция delay_ms гене- рирует задержку 5000 мс. В строке 106 находится ключевое слово else оператора if-else, означающее, что следующий оператор будет выполняться, если условие оператора if-else (строка 88) ложно.
7.9. Проект «Dsl820» 521 В строке 107 реализован бесконечный цикл с помощью оператора while (см. Оператор while). Этот оператор будет выполняться до тех пор, пока выраже- ние в скобках истинно, т. е. не 0. Выражение в скобках — 1 (можно также напи- сать 15 или 8), а не 0, т. е. всегда истинно. Следовательно, этот оператор будет вы- полняться бесконечно. В строке 111 также реализован бесконечный цикл с помощью оператора while. В этом цикле бесконечно будет выполняться группа операторов, заключённая в фигурные скобки, находящиеся в строках 112 (открывающая скобка) и 121 (за- крывающая скобка). В строке 113 находится оператор for (см. Оператор for). Тело этого оператора, заключённое в фигурные скобки, находящиеся в строках 114 (открывающая скобка) и 120 (закрывающая скобка), будет выполняться для каждого устройства DS1820/DS 18S20, т. е. столько раз, сколько устройств подключено к шине 1-Wire. В строке 115 вызывается функция dsl820_temperature_10 с фактическим па- раметром &rom_code[i,0] (адрес ROM-кода i-ro датчика). Эта функция возвраща- ет температуру i-ro датчика DS1820/DS 18S20. Это значение присваивается пере- менной temp. Температура выражена в °C и умножена на 10 (см. Функции темпе- ратурного датчика DS1820/DS18S20 от Dallas Semiconductor). Эта функция определена в подключённом файле dsl820.h (строка 56). В строке 116 функция sprintf осуществляет форматные преобразования и по- мещает результаты в строку Icdjbuffer. Спецификация преобразования «t%u=%i.%u\xdfC» означает, что сначала будет выведен символ «t», затем значе- ние выражения ++i (т. е. значение переменной i, увеличенное на 1) в формате де- сятичного целого беззнакового числа (символ преобразования ’и’). После этого будет выведен символ «=», значение выражения temp/10 в формате десятичного целого числа со знаком (символ преобразования Т), т. е. целая часть результата этого выражения. Затем будет выведен символ «.», значение выражения abs(temp%10) в формате десятичного целого беззнакового числа (символ преобра- зования ’и’), т. е. абсолютное значение остатка от деления temp/10, и, наконец, последовательность «°C» (управляющая последовательность \xdf означает, что бу- дет выведен символ с кодом df, т. е. символ «°») (см. Стандартные функции вво- да/вывода). В строке 117 осуществляется вызов функции lcd_clear, которая снова очищает LCD и устанавливает позицию для вывода символа в ряд 0 столбца 0. В строке 118 функция lcd_puts отображает в текущей дисплейной позиции со- держимое строки Icdjbuffer, которая была сформирована в 116-й строке программы. В строке 119 функция delay_ms генерирует задержку 800 мс. В строке 122 закрывающая фигурная скобка указывает конец тела функции main. Программа работает следующим образом. После подачи питания (или аппа- ратного сброса на выводе RESET микроконтроллера) начинает выполняться функция main. Последовательно вызываются функция Icdjnit, которая инициа- лизирует LCD-модуль, и функция lcd_putsf, которая в верхнюю строку LCD-мо- дуля выводит последовательность «CodeVisionAVR», а в нижнюю — «1 Wire Bus Demo» (Рис. 7.24).
522 Глава 7. Примеры проектов vcc Д R1 4к7 DD1 DS1820/DS18S20 DD2 DS1820/DS18S20 DD3 DS1820/DS18S20 DD4 AT90S8515 РВО/ТО РВ1/Т1 PB2/AJN0 PB3/AJN1 PB4/SS P85/MOSI PB6/MISO PB7/SCK PD0/RXD PD1/TXD PD2/1NT0 РОЗ/) NT 1 PD4 PD5/OC1A PD6 W4 PO7/RO PA0/AD0 РА1/АО1 РА2/АО2 PA3/AD3 PA4/AD4 РА5/АО5 PA6/AD6 РА7/АО7 РС0/А8 РС1/А9 РС2/А10 РСЗ/А11 РС4/А12 РС5/А13 РС6/А14 РС7/А15 Рис. 7.24. Результат работы программы — вывод последовательности «CodeVisioiiAVR\nl Wire Bus Demo». После задержки в 2000 мс дисплей очищается. Затем с помощью функции wl_search определяется количество устройств DS 1820/DS18S20, подключённых к шине 1-Wire, и полученное значение с помощью функций sprintf и lcd_puts выво- дится на LCD. Если ни одно устройство не обнаружено, то на LCD выводится соответствую- щая информация (Рис. 7.25), программа переходит к выполнению бесконечного цикла while (строка 107) и больше ничего не делает. DD1 AT90S8515 XTAL1 XTAL2 RESET РВО/ТО РВ1Д1 PB2/AIN0 PB3/AIN1 P84/SS PB5/MOSI PB6/MISO PB7/SCK PDO/RXD PD1/TXD PD2/1NT0 РОЗ/INTI PD4 PD5/OC1A PD6/WR PD7/RO PA0/AD0 PA1/AD1 PA2/AD2 PA3/AD3 PA4/AD4 PA5/AD5 PA6/AD6 PA7/AD7 РС0/А8 РС1/А9 РС2/А10 РСЗ/А11 РС4/А12 РС5/А13 РС6/А14 РС7/А15 ALE ОС1В ICP 39 38 37 36 33 21 22 2 23_____3_ 24 25_____4_ 26 5 27_____6_ 28 7 30 29 31 Рис. 7.25. Результат работы программы — не обнаружено ни одного устройства DS1820. Если обнаружено хотя бы одно устройство, то на LCD выводится информация об их количестве (Рис. 7.26), и после задержки в 2000 мс дисплей очищается.
7.9. Проект «Ds1820» 523 vcc 4 DD4 AT90S8515 DD2 DS1820/DS18S20 --— VDD DO —---- GND -4^ GND DD3 DS1820/DS18S20 XTAM XTAL2 РВ0Д0 PB1/T1 PB2/AIN0 PB3/AIN1 PB4/SS PB5/MOSI pb6/miso PB7/SCK PDO/RXD PD1/TXD PD2/1NT0 PO3/INT1 PD4 PD5/OC1A PD6/WR PD7/RD PAO/ADO PA1/AD1 PA2/AD2 PA3/AD3 PA4/AD4 PA5/AD5 PA6/AD6 PA7/AD7 PC0/A8 PC1/A9 PC2/A10 PC3/A11 PC4/A12 PC5/A13 PC6/A14 PC7/A15 OC1B ICP Puc. 7.26. Результат работы программы — вывод информации о количестве обнаруженных устройств DS1820. После этого для каждого обнаруженного устройства DS 1820/DS18S20 сначала выводится соответствующая надпись (Рис. 7.27), а затем, через 2000 мс, соответствующий ROM-код (Рис. 7.28). vcc 4 R1 4к7 DD1 DS1820/DS18S20 DD4 AT90S8515 XTAL1 XTAL2 RESET DD2 DS1820/DS18S20 РВО/ТО РВ1/Т1 PB2/AIN0 PB3/AIN1 PB4/SS PB5/MOSI PB6/MISO PB7/SCK PAO/ADO PA1/AD1 PA2/AD2 PA3/AD3 PA4/AD4 PA5/AD5 PA6/AD6 PA7/AD7 DD3 DS1820/DS18S20 PD0/RXD РО1ЛХО PD2/INTO PD3/1NT1 PD4 PDS/OC1A PD6/WR PD7/RD РС0/А8 РС1/А9 РС2/А10 РСЗ/А11 РС4/А12 РС5/А13 РС6/А14 РС7/А15 ALE ОС1В ICP Рис. 7.27. Результат работы программы — вывод надписи для устройства № 1.
524 Глава 7. Примеры проектов vcc 4 R1 4к7 DD1 DS1820/DS18S20 DD4 AT9OS8515 DD2 DS1820/DS18S20 DD3 DS1820/DS18S20 XTAL1 XTAL2 RESET PBO/TO PB1/T1 PB2/AIN0 PB3/AIN1 PB4/SS PB5/MOSI PB6/MISO PB7/SCK POO/RXD PD1/TXD PO2/INT0 PO3/1NT1 PD4 PD5/OC1A PD6/WR PO7/RD PAO/ADO PA1/AD1 PA2/AD2 PA3/AD3 PA4/AD4 PA5/AD5 PA6/AD6 PA7/AO7 PC0/A8 PC1/A9 PC2/A10 PC3/A11 PC4/A12 PC5/A13 PC6/A14 PC7/A15 ocib icp -21— Puc. 7.28. Результат работы программы — вывод ROM-кода устройства № 1. Через 5000 мс будет выведена подобная информация для следующего устройства и т. д., вплоть до последнего. После этого программа переходит в бесконечный цикл отображения измерен- ной температуры. При этом поочередно, через каждые 800 мс, считывается и вы- водится на LCD значение температуры, измеренное каждым датчиком (Рис. 7.29). Дойдя до последнего, измерение опять начинается с первого и т. д. vcc 4 R1 4к7 DD1 DS1820/DS18S20 DD2 DS1820/DS18S20 DD3 DS 1820/DS 18S20 DD4 AT90S8515 XTAL1 XTAL2 RESET PB0/T0 PB1/T1 PB2/AIN0 PB3/AIN1 PB4/SS PB5/MOSI PB6/MISO PB7/SCK PDO/RXD PD1.TXD PD2/1NT0 PO3/1NT1 PD4 PO5/OC1A PO6/WR PO7/RD PAO/ADO PA1/AD1 PA2/AD2 PA3/AD3 PA4/AD4 PA5/AD5 PA6/AD6 PA7/AD7 OCIB ICP PC0/A8 PC1/A9 PC2/A10 PC3/A11 PC4/A12 PC5/A13 PC6/A14 PC7/A15 Puc. 7.29. Результат работы программы — индикация температуры, измеренной устройством № 1.
7.10. Проект «Thermlcd» 525 Обратите внимание, что LCD-модуль, который имеет знакогенератор с буква- ми кириллицы, не имеет символа «°». Информация о температуре на таком моду- ле будет выглядеть так как показано на Рис. 7.30, т. к. код Oxdf (см. строку 116) в этом модуле соответствует символу «» (см. Табл. 5.32). vcc Ж R14k7 DD4 AT90S8515 DD1 DS1820/DS18S20 DD2 DS1820/DS18S20 DD3 DS1820/DS18S20 XTAL1 XTAL2 RESET PBO/TO PB1/T1 P82/AIN0 PB3/AIN1 P84/SS PB5/MOSI PB6/MISO PB7/SCK POO/RXD PD1/TXD PO2/INT0 PD3/INT1 PD4 PO5/OC1A PD6/WR PD7/RD PAO/ADO PA1/AD1 PA2/AD2 PA3/AD3 PA4/AD4 PA5/AD5 PA6/AD6 PA7/AD7 PC0/A8 PC1/A9 PC2/A10 PC3/A11 PC4/A12 PC5/A13 PC6/A14 PC7/A15 OC1B ICP RV1 LCD1 VCC GND Puc. 7.30. Результат работы программы при использовании LCD-модуля, который имеет знакогенератор с буквами кириллицы. При использовании такого LCD-модуля можно порекомендовать самостоя- тельно определить символ «°» (см., например, Проект «Lcdchar»). При отсутствии отладочной платы STK500 можно самостоятельно реализо- вать этот проект по приведённой схеме. Цепь питания, сброса и кварцевый резо- натор микроконтроллера не показаны (см. Рис. 4.2). 7.10. Проект «Thermlcd» Этот пример демонстрирует термометр, реализованный на температурном датчике LM75 шины 12 * 4 5 б: 7 8С от National Semiconductor. Информация выводится на алфавитно-цифровой LCD-модуль 2x16. В этом проекте содержится один исходный файл — thermlcd.c. Текст этого файла с русифицированными комментариями: 2: Термометр с LCD-дисплеем, использующий 3 ; температурный датчик LM75 шины 12С от 4: National Semiconductor 5: б: CodeVisionAVR С Compiler 7: (С) 2000-2002 HP InfoTech S.R.L. 8: www.hpinfotech.ro
526 Глава 7. Примеры проектов 9: 10: Чип: AT90S8515 11: Модель памяти: SMALL 12: Размер стека данных: 128 Б 13: */ 14: 15: // Для шины I2C используются биты ПОРТА А 16: // ПРИМЕЧАНИЕ: ДЛЯ СИГНАЛОВ SDA и SCL ШИНЫ I2C 17: // ДОЛЖНЫ БЫТЬ ИСПОЛЬЗОВАНЫ РЕЗИСТОРЫ 3.3...4.7к 18: // ПОДТЯНУТЫЕ К +5 В 19: 20: #asm 21: .equ _i2c_port=0xlb ;ПОРТ А 22: .equ _scl_bit=0 ;SCL=STK500 РАЗЪЁМ PORTA вывод 1 23: .equ _sda_bit=l ;SDA=STK500 РАЗЪЁМ PORTA вывод 2 24: #endasm 25: 26: #include <lm75.h> // программы управления LM75 27: ^include <stdio.h>- // функция sprintf 28: #include <delay.h> // функция delay_ms 29: 30: /* 31: Используется алфавитно-цифровой LCD 2x16, подключённый 32: к разъёму PORTC STK500 следующим образом: 33: 34: [LCD] [разъём PORTC STK500] 35: 1 GND - 9 GND 36: 2 +5V - 10 VCC 37: 3 VLC - управление контрастностью LCD напряжение 0...1 В 38: 4 RS - 1 PC0 39: 5 RD - 2 PCI 40: 6 EN - 3 PC2 41: 11 D4 - 5 PC4 42: 12 D5 - 6 PC5 43: 13 D6 - 7 PC6 44: 14 D7 - 8 PC7 45: */ 46: 47: #asm 48: .equ ______lcd_port=0xl5 49: ttendasm 50: 51: ^include <lcd.h> // программы управления LCD 52: 53: // буфер LCD-дисплея 54: char lcd_buffer[33] ; 55: 56: void main(void) 57: { 58: char sign;
7.10. Проект «Thermlcd» 527 59: int temp; 60: // инициализируем LCD 61: lcd_init(16); 62: // инициализируем шину I2C 63: i2c_init(); 64: // инициализируем LM75 чип с адресом 0 65: // гистерезис температуры 20’С 66: // 0.S. температура 2 5’С 67: // O.S. выход активный - высокий уровень 68: lm75_init(0,20,25,1); 69: // цикл отображения температуры 70: while (1) 71: { 72: // читаем температуру LM75 *10‘С 73: //из чипа с адресом 0 74: temp=lm75_temperature_10(0) ; 75: sign=' +' ; 76: if (temp<0) 77: { 78: sign='-'; 79: temp=-temp; 80: }; 81: sprintf(lcd_buffer,"t=%c%i.%u\xdfC",sign,temp/10,temp%10) 82: // отобразим температуру 83: lcd_clear(); 84: lcd_puts(lcd_buffer) ; 85: delay_ms(200); 86: } 87: } В этом файле знаками /* (начало) и */ (конец) выделены блоки с коммента- риями, а знаком // — строки с комментариями (см. Комментарий). В блоке с комментариями (строки 1... 13) даётся общая информация о проекте. В строках с комментариями 15... 18 даются рекомендации по подключению температурного датчика. В строках 20...24 объявляется, какой порт микроконтроллера и какие биты порта будут использованы для связи с шиной 12С. Для этого в программу включён ассемблерный код. В строке 20 директива #asm говорит компилятору о начале ас- семблерного кода, а в строке 24 директива #endasm — о его завершении (см. Ди- рективы #asm и ttendasm). В строке 21 директива ассемблера .equ присваивает зна- чение идентификатору___i2c_port. Это значение, соответствующее адресу регист- ра PORTx выбранного порта (в данном случае выбран Порт А), находим в файле 90s8515.h в соответствующей строке: «sfrb PORTA = 0x1b;». Этот файл находится в поддиректории ..\INC. Обратите внимание, что комментарии в ассемблерной части кода отде- ляются точкой с запятой (;). Иначе будет выдаваться ошибка при ассем- блировании (при построении проекта).
528 Глава 7. Примеры проектов В строках 22, 23 директивы ассемблера .equ присваивают значение идентифи- каторам __scl__bit и_sda__bit. Эти значения соответствуют номерам битов вы- бранного порта, которые будут использоваться для сигналов SCL и SDA соответ- ственно. При трансляции ассемблерного кода, полученного при компиляции данного проекта компилятором Си CodeVisionAVR, ассемблер вместо этих идентификато- ров подставит их значения. В случае модификации программы при использовании другого порта и/или битов порта достаточно будет лишь заменить значения в указанных директивах .equ, не изменяя остального текста программы. Директивами #include (строки 26...28) подключаются: функции LM75 — файл lm75.h (см. Функции температурного датчика LM75 от National Semiconductor)', функции ввода/вывода — файл stdio.h (см. Стандартные функции ввода/вывода) и функции задержки — файл delay.h (см. Функции задержки). Перед компиляцией препроцессор компилятора вставит вместо этих строк текст соответствующих файлов (см. Директива ^include). В следующем блоке с комментариями (строки 30...45) даются рекомендации по подключению к отладочной плате STK500 алфавитно-цифрового LCD-модуля 2x16. В строках 47...49 объявляется, какой порт микроконтроллера будет использо- ваться для подключения LCD-модуля. Для этого в программу включён ассемб- лерный код. В строке 47 директива #asm говорит компилятору о начале ассемб- лерного кода, а в строке 49 директива #endasm — о его завершении (см. Директи- вы #asm и ttendasm). В строке 48 директива ассемблера .equ присваивает значение идентификатору ___ledport. Это значение, соответствующее адресу регистра PORTx выбранного порта (в данном случае выбран Порт С), находим в файле 90s8515.h в соответствующей строке: «sfrb PORTC=Oxl5;». При трансляции ас- семблерного кода, полученного при компиляции данного проекта компилятором Си CodeVisionAVR, ассемблер вместо этого идентификатора подставит его значе- ние. В случае модификации программы при использовании другого порта для под- ключения LCD-модуля достаточно будет лишь заменить значение в указанной директиве .equ, не изменяя остального текста программы. Подробнее о директивах ассемблера см. систему помощи бесплатной про- граммы AVR Studio от Atmel, которую можно скачать на сайте разработчиков мик- роконтроллеров AVR http://www.atmel.com. Директивой #include (строка 51) подключаются LCD-функции — файл led.h (см. LCD-функции для дисплеев до 2x40 символов). Перед компиляцией препроцес- сор компилятора вставит вместо этой строки текст соответствующего файла (см. Директива ^include). В строке 54 объявляется глобальный символьный массив (строковая перемен- ная) ledjbuffer, состоящий из 33 членов. Этот массив будет расположен в SRAM микроконтроллера. При этом все элементы этого массива автоматически иници- ализируются со значением 0 (см. Массивы). В этом массиве будет храниться ин- формация, предназначенная для вывода на LCD.
7.10. Проект «Thermlcd» 529 Строки 56...87 — это определение основной функции программы (main). Эта функция обязательно должна присутствовать во всех программах (но только ОДНА!). Именно с этой функции, в каком бы месте программы она ни находи- лась, начинается выполнение программы. В строке 56 осуществляется объявление функции main. Вначале стоит ключе- вое слово void, указывающее на то, что эта функция не возвращает никаких зна- чений. Затем следует имя функции main (ЕГО ИЗМЕНЯТЬ НЕЛЬЗЯ!) и ключевое слово void в скобках, которое указывает на то, что в эту функцию не передаются никакие параметры (см. Функций). В строке 57 открывающая фигурная скобка указывает на начало тела функции main. В строке 58 объявляется локальная символьная переменная sign (типа char). Так как в настройках проекта на закладке С Compiler (Компилятор Си) выбра- на опция char is unsigned (байт является беззнаковым) (Рис. 7.31), то все данные типа char (байт) компилятор будет обрабатывать как данные типа unsigned char (беззнаковый байт) (см. Закладка С Compiler (Компилятор Си)). То есть глобаль- ный массив lcd_buffer (строка 54) будет состоять из данных типа unsigned char, а локальная переменная sign будет иметь тип unsigned char. Рис. 7.31. Опция char is unsigned (байт является беззнаковым) выбрана.
530 и Глава 7. Примеры проектов Настоятельно рекомендуется явно определять тип данных в тексте про- граммы, т. к. при распечатке текста программы настроек проекта не видно. Это может вызвать неправильное понимание работы программы! В строке 59 объявляется локальная переменная temp типа int (целое) (см. Типы данных). В строке 61 осуществляется вызов функции Icdjnit с фактическим парамет- ром 16 (количество столбцов в используемом модуле LCD). Эта функция инициа- лизирует модуль LCD, очищает дисплей и устанавливает позицию для вывода символа в ряд 0 столбца 0. Курсор не отображается. Эта функция определена в подключённом файле led.h (строка 51). В строке 63 осуществляется вызов функции i2c__init, которая инициализирует шину 12С. Эта функция определена в файле i2c.h, который подключается дирек- тивой #include в подключённом файле lm75.h (строка 26). В строке 68 осуществляется вызов функции lm75_init с фактическими пара- метрами 0 (адрес чипа LM75), 20 (предел thyst), 25 (предел tos) и 1 (параметр pol, т. е. активным уровнем O.S. выхода LM75 является высокий). Эта функция ини- циализирует чип LM75 (см. Функции температурного датчика LM75 от National Semiconductor). Она определена в подключённом файле lm75.h (строка 26). В строке 70 реализован бесконечный цикл с помощью оператора while (см. Оператор while). Этот оператор будет выполняться до тех пор, пока выраже- ние в скобках истинно, т. е. не 0. Выражение в скобках — 1 (можно также напи- сать 3 или 27), а не 0, т. е. всегда истинно. Следовательно, этот оператор будет вы- полняться бесконечно. В этом цикле бесконечно будет выполняться группа операторов, заключённая в фигурные скобки, находящиеся в строках 71 (открывающая скобка) и 86 (за- крывающая скобка). В строке 74 осуществляется вызов функции lm75_temperature_10 с фактичес- ким параметром 0 (адрес чипа LM75). Эта функция возвращает температуру дат- чика LM75 с адресом 0. Температура выражена в °C и умножена на 10 (см. Функ- ции температурного датчика LM75 от National Semiconductor). Эта функция опре- делена в подключённом файле lm75.h (строка 26). В строке 75 символьной переменной sign присваивается значение символа (т. е. числовой код символа ’+’) (см. «Переменные»). В строках 76...80 находится укороченный оператор if-else (без else) (см. Опера- тор if-else). В строке 76 — начало этого оператора, на что указывает ключевое слово if. Вы- ражение в скобках (условие оператора if-else) будет истинно, если значение пере- менной temp меньше нуля (т. е. отрицательное). В противном случае выражение ложно. Если выражение в скобках истинно, то будет выполняться группа операторов (составной оператор), заключённая в фигурные скобки, находящиеся в строках 77 (открывающая скобка) и 80 (закрывающая скобка). После этого выполнение программы будет продолжено со строки 81. Если выражение в скобках ложно, то выполнение программы будет СРАЗУ продолжено со строки 81.
7.10. Проект «Thermlcd» 531 В строке 78 символьной переменной sign присваивается значение символа (т. е. числовой код символа В строке 79 символьной переменной temp присваивается её арифметическое отрицание (знак операции см. Унарные операции), т. е. значение, противопо- ложное по знаку. В строке 81 функция sprintf осуществляет форматные преобразования и поме- щает результаты в строку Icdjbuffer. Спецификация преобразования «t=%c%i.%u\xdfC» означает, что сначала будет выведена последовательность «t=», затем значение переменной sign в символьном виде (символ преобразова- ния ’с’). После этого будет выведено значение выражения temp/10 в формате деся- тичного целого числа со знаком (символ преобразования Т), т. е. целая часть ре- зультата этого выражения. Затем будет выведен символ «.», значение выражения temp%10 в формате десятичного целого беззнакового числа (символ преобразова- ния ’и’), т. е. значение остатка от деления temp/10, и, наконец, последователь- ность «°C» (управляющая последовательность \xdf означает, что будет выведен символ с кодом df, т. е. символ «°»). Эта функция определена в подключённом файле stdio.h (строка 27) (см. Стандартные функции ввода/вывода). В строке 83 осуществляется вызов функции lcd_clear, которая очищает LCD и устанавливает позицию для вывода символа в ряд 0 столбца 0. В строке 84 функция lcd_puts отображает в текущей дисплейной позиции со- держимое строки Icdjbuffer, которая была сформирована в 81-й строке програм- мы. Обе эти функции определены в подключённом файле led.h (строка 51). В строке 85 функция delay_ms генерирует задержку 200 мс (см. Функции за- держки). Эта функция определена в подключённом файле delay.h (строка 28). В строке 87 закрывающая фигурная скобка указывает на конец тела функции main. Программа работает следующим образом. После подачи питания (или аппа- ратного сброса на выводе RESET микроконтроллера) начинает выполняться функция main. Последовательно вызываются функция Icdjnit, которая инициа- лизирует LCD-модуль, функция ilcjnit, которая инициализирует шину 12С, и функция lm75Jnit, которая инициализирует чип LM75. Затем программа переходит к выполнению бесконечного цикла, в котором значение температуры считывается с датчика LM75 с помощью функции lm75_temperature_10. Полученная информация обрабатывается с помощью функ- ции sprintf и выводится на LCD-модуль с помощью функции led puts. Затем дела- ется задержка в 200 мс, и цикл повторяется сначала. Результат работы программы показан на Рис. 7.32. Обратите внимание, что LCD-модуль, который имеет знакогенератор с буква- ми кириллицы, не имеет символа «°». Информация о температуре на таком моду- ле будет выглядеть, как показано на Рис. 7.33, т. к. код Oxdf (см. строку 81) в этом модуле соответствует символу «» (см. Ткбл. 5.32). При использовании такого LCD-модуля можно порекомендовать самостоя- тельно определить символ «°» (см., например, Проект «Lcdchar).
532 Глава 7. Примеры проектов R1 vcc <9- DD2 AT90S8515 R1...R2 = 3.3 к...4.7 к XTAL1 XTAL2 RESET РВОДО РВ1/Т1 PB2/A1N0 PB3/AIN1 PB4/SS PB5/MOSI PBB/MISO PB7/SCK PDO/RXD PD1/TXD PD2/1NT0 PD3/INT1 PD4 PD5/OC1A PD6/WR PD7/RD PAO/ADO PA1/AD1 PA2/AD2 РАЗ/АОЗ PA4/AD4 РА5/АО5 PA6/AD6 PA7/AD7 РСО/Ав РС1/А9 РС2/А10 РСЗ/А11 РС4/А12 PCS/A13 РС6/А14 РС7/А15 ALE OCIB ЮР Рис. 7.32. Результат работы программы — индикация температуры, измеренной датчиком LM75. R1 VCC DD2 AT90S8515 R1...R2 = 3.3 к...4.7 к РВО/ТО РВ1Д1 PB2/AIN0 PB3/AIN1 P84/SS PB5/MOSI PB6/MISO P87/SCK PDO/RXD PD1/TXD PD2/1NT0 PD3/INT1 PD4 РО5/ОС1А PD6/WR PD7/RD PAO/ADO PA1/AD1 PA2/AD2 PA3/AD3 PA4/AD4 PA5/ADS PA6.-AD6 PA7/AD7 ALE OCIB ICP РСО/Ав РС1/А9 РС2/А10 РСЗ/А11 РС4/А12 РС5/А13 РС6/А14 РС7/А15 Рис. 7.33. Результат работы программы при использовании LCD-модуля, который имеет знакогенератор с буквами кириллицы. При отсутствии отладочной платы STK500 можно самостоятельно реализо- вать этот проект по приведённой схеме. Цепь питания, сброса и кварцевый резо- натор микроконтроллера не показаны (см. Рис. 4.2). 7.11. Проект «Therm75» Этот пример демонстрирует термометр с последовательной передачей данных по RS232, реализованный на температурном датчике LM75 шины 12С от National Semiconductor. В этом проекте содержится один исходный файл — therm75.c. Текст этого файла с русифицированными комментариями:
7.7 7. Проект « Therm 75» 533 1: /* 2: Термометр с последовательной передачей данных RS232, использующий 3: температурный датчик LM75 шины I2C от National Semiconductor 4: 5: Чип: AT90S8515 б: Модель памяти: SMALL 7: Размер стека данных: 128 Б 8: Тактовая частота: 3.6864 МГц 9: Параметры связи: 9600 8N1 10: 11: CodeVisionAVR С Compiler 12: (С) 2000-2002 HP InfoTech S.R.L. 13: www.hpinfotech.ro 14: 15: Для того чтобы использовать разъём 16: RS232 SPARE на STK500, должны быть 17: сделаны следующие соединения: 18: [разъём RS232 SPARE] [Разъём PORTD] 19: RXD - 1 PD0 20: TXD - 2 PD1 21: 22: Для шины I2C используются биты ПОРТА А 23: ПРИМЕЧАНИЕ: ДЛЯ СИГНАЛОВ SDA и SCL ШИНЫ I2C ДОЛЖНЫ 24: БЫТЬ ИСПОЛЬЗОВАНЫ РЕЗИСТОРЫ 3.3...4.7к, ПОДТЯНУТЫЕ К + 5В 25: 26: */ 27: 28: #asm 29: .equ ______i2c_port=0xlb ;ПОРТ А 30: .equ ______scl_bit=0 ;SCL=STK500 РАЗЪЁМ PORTA вывод 1 31: .equ ______sda_bit=l ;SDA=STK500 РАЗЪЁМ PORTA вывод 2 32: #endasm 33: 34: // программы управления LM75 35: #include <lm75.h> 36: // функция printf 37: #include <stdio.h> 38: // определения регистров ввода/вывода AT90S8515 39: #include <90s8515.h> 40: #include <delay.h> 41: 42: void main(void) 43: { 44: char sign; 45: int temp; 46: // разрешим передатчик 47: UCR=8; 48: // Скорость передачи = 9600 при 3.6864 МГц 49: UBRR=23;
534 Глава 7. Примеры проектов 50: // инициализируем шину I2C 51: i2c_init(); 52: // инициализируем LM75 чип с адресом 0 53: // гистерезис температуры 20’С 54: // O.S. температура 25‘С 55: // O.S. выход активный - высокий уровень 56: lm75_init(0,20,25,1); 57: // цикл передачи температуры 58: while (1) 59: { 60: // читаем температуру LM75 *10'С 61: //из чипа с адресом 0 62: temp=lm75_temperature_10(0); 63: sign='+'; 64: if (temp<0) 65: { бб: sign='-'; 67: temp=-temp; 68: }; 69: // пошлём измеренную температуру через 70: // последовательную связь RS232 71: printf("t=%c%i.%u\xf8C\r\n", sign,temp/10,temp%10); 72: delay_ms(200); 73: } 74: } В этом файле знаками /* (начало) и */ (конец) выделены блоки с коммента- риями, а знаком // — строки с комментариями (см. Комментарий). В блоке с комментариями (строки 1 ...26) даются общая информация о проек- те и рекомендации по подключению температурного датчика и использованию разъёма RS232 SPARE на плате STK500. В строках 28...32 объявляется, какой порт микроконтроллера и какие биты порта будут использованы для связи с шиной 12С. Для этого в программу включён ассемблерный код. В строке 28 директива #asm говорит компилятору о начале ас- семблерного кода, а в строке 32 директива #endasm — о его завершении (см. Ди- рективы #asm и ttendasm). В строке 29 директива ассемблера .equ присваивает зна- чение идентификатору___i2c_port. Это значение, соответствующее адресу регист- ра PORTx выбранного порта (в данном случае выбран Порт А), находим в файле 90s8515.h в соответствующей строке: «sfrb PORTA=Oxlb;». Этот файл находится в поддиректории ..\INC. В строках 30, 31 директивы ассемблера .equ присваивают значение идентифи- каторам __scljbit и_sda bit. Эти значения соответствуют номерам битов, кото- рые будут использоваться для сигналов SCL и SDA соответственно. При трансляции ассемблерного кода, полученного при компиляции данного проекта компилятором Си CodeVisionAVR, ассемблер вместо этих идентификато- ров подставит их значения.
7.11. Проект « Therm 75» 535 В случае модификации программы при использовании другого порта и/или битов порта достаточно будет лишь заменить значения в указанных директивах .equ, не изменяя остального текста программы. Обратите внимание, что комментарии в ассемблерной части кода отде- ляются точкой с запятой (;). Иначе будет выдаваться ошибка при ассем- блировании (при построении проекта). Подробнее о директивах ассемблера см. систему помощи бесплатной про- граммы AVR Studio от Atmel, которую можно скачать на сайте разработчиков мик- роконтроллеров AVR http://www.atmel.com. Директивами #include (строки 35...40) подключаются: функции LM75 — файл lm75.h (см. Функции температурного датчика LM75 от National Semiconductor), функции ввода/вывода — файл stdio.h (см. Стандартные функции ввода/вывода), заголовочный файл для используемого микроконтроллера AVR (AT90S8515) — файл 90s8515.h, и функции задержки — файл delay.h (см. Функции задержки) со- ответственно. Перед компиляцией препроцессор компилятора вставит вместо этих строк текст соответствующих файлов (см. Директива ^include). Строки 42...74 — это определение основной функции программы (main). Эта функция обязательно должна присутствовать во всех программах (но только ОДНА!). Именно с этой функции, в каком бы месте программы она ни находи- лась, начинается выполнение программы. В строке 42 осуществляется объявление функции main. Вначале стоит ключе- вое слово void, указывающее на то, что эта функция не возвращает никаких зна- чений. Затем следует имя функции main (ЕГО ИЗМЕНЯТЬ НЕЛЬЗЯ!) и ключевое слово void в скобках, которое указывает на то, что в эту функцию не передаются никакие параметры (см. Функции). В строке 43 открывающая фигурная скобка указывает на начало тела функции main. В строке 44 объявляется локальная символьная (типа char) переменная sign. Так как в настройках проекта на закладке С Compiler (Компилятор Си) выбра- на опция char is unsigned (байт является беззнаковым) (Рис. 7.34), то все данные типа char (байт) компилятор будет обрабатывать как данные типа unsigned char (беззнаковый байт) (см. Закладка С Compiler (Компилятор Си)). То есть локальная переменная sign будет иметь тип unsigned char. Настоятельно рекомендуется явно определять тип данных в тексте про- граммы, т. к. при распечатке текста программы настроек проекта не видно, что может вызвать неправильное понимание работы программы! В строке 45 объявляется локальная переменная temp типа int (целое) (см. Типы данных). В строках 47, 49 осуществляется инициализация асинхронного приёмопере- датчика UART. В строке 47 в регистр UCR (регистр управления универсальным асинхронным приёмопередатчиком UART) записывается значение 8, тем самым разрешается ра- бота передатчика UART, посылки 8-битовые, 1 столовый бит без контроля чётности.
536 Глава 7. Примеры проектов Рис. 7.34. Опция char is unsigned (байт является беззнаковым) выбрана. В строке 49 в регистр UBRR (регистр скорости передачи универсального асинхронного приёмопередатчика UART) записывается значение 23, тем самым задаётся скорость передачи UART. При тактовой частоте 3.6864 МГц значению 23 в регистре UBRR соответствует скорость передачи 9600 бод. Подробнее о работе приёмопередатчика UART см. описание (datasheet) от Atmel на используемый микроконтроллер (см. Команда Help -> AVR Data Sheets (Помощь -> Описания AVR)). В строке 51 осуществляется вызов функции i2c_init, которая инициализирует шину 12С. Эта функция определена в файле i2c.h, который подключается дирек- тивой #include в подключённом файле lm75.h (строка 35). В строке 56 осуществляется вызов функции lm75_init с фактическими парамет- рами 0 (адрес чипа LM75), 20 (предел thyst), 25 (предел tos) и 1 (параметрpol, т. е. ак- тивным уровнем O.S. выхода LM75 является высокий). Эта функция инициализи- рует чип LM75. Она определена в подключённом файле lm75.h (строка 35). Подроб- нее см. Функции температурного датчика LM75 от National Semiconductor. В строке 58 реализован бесконечный цикл с помощью оператора while (см. Оператор while). Этот оператор будет выполняться до тех пор, пока выраже- ние в скобках истинно, т. е. не 0. Выражение в скобках — 1 (можно также напи- сать 22 или 96), а не 0, т. е. всегда истинно. Следовательно, этот оператор будет выполняться бесконечно.
7.7 7. Проект «Therm 75» 537 В этом цикле бесконечно будет выполняться группа операторов, заключённая в фигурные скобки, находящиеся в строках 59 (открывающая скобка) и 73 (за- крывающая скобка). В строке 62 осуществляется вызов функции lm75_temperature_10 с фактичес- ким параметром 0 (адрес чипа LM75). Эта функция возвращает температуру дат- чика LM75 с адресом 0. Температура выражена в °C и умножена на 10. Эта функ- ция определена в подключённом файле lm75.h (строка 35). Подробнее см. Функ- ции температурного датчика LM75 от National Semiconductor. В строке 63 символьной переменной sign присваивается значение символа *+* (т. е. числовой код символа *+*) (см. Переменные). В строках 64...68 находится укороченный оператор if-else (без else) (см. Опера- тор if-else). В строке 64 начало этого оператора, на что указывает ключевое слово if. Если выражение в скобках истинно, то будет выполняться группа операторов (состав- ной оператор), заключённая в фигурные скобки, находящиеся в строках 65 (от- крывающая скобка) и 68 (закрывающая скобка). После этого выполнение про- граммы будет продолжено со строки 71. Если выражение в скобках ложно, то вы- полнение программы будет СРАЗУ продолжено со строки 71. Выражение в скобках будет истинно, если значение переменной temp меньше нуля (т. е. отрицательное). В противном случае выражение ложно. В строке 66 символьной переменной sign присваивается значение символа (т. е. числовой код символа ’-’). В строке 67 символьной переменной temp присваивается её арифметическое отрицание (знак операции -, см. Унарные операции), т. е. значение, противопо- ложное по знаку. В строке 71 функция printf осуществляет форматные преобразования и пере- даёт строку символов через UART (RS232). Спецификация преобразования «t = %c%i.%u\xf8C\r\n» означает, что сначала будет передана последовательность «t =», затем будет передано значение переменной sign в формате символа (символ преобразования 'с'). Затем будет передано значение выражения temp/10 в формате десятичного целого числа со знаком (символ преобразования Т), т. е. целая часть результата этого выражения. После этого будет передан символ «.» и значение вы- ражения temp%10 в формате десятичного целого беззнакового числа (символ пре- образования 'и'), т. е. значение остатка отделения temp/10. В заключение будет передан символ с кодом 0xf8 (управляющая последова- тельность \xf8 означает, что будет передан символ с кодом 0xf8), символ «С» и два символа: \г — символ возврата каретки и \п — символ перевода строки. Благода- ря этим символам каждое новое значение выводится с новой строки. Функция printf определена в подключённом файле stdio.h (строка 37). Подроб- нее см. Стандартные функции ввода/вывода. В строке 72 функция delay_ms генерирует задержку 200 мс (см. Функции задерж- ки). Эта функция определена в подключённом файле delay.h (строка 40). В строке 74 закрывающая фигурная скобка указывает на конец тела функции main. Программа работает следующим образом. После подачи питания (или аппарат- ного сброса на выводе RESET микроконтроллера) начинает выполняться функция main. При этом объявляются переменные temp и sign и проводится инициализация
538 Глава 7. Примеры проектов UART. Затем последовательно вызываются функция i2c_init, которая инициализи- рует шину 12С, и функция lm75_init, которая инициализирует чип LM75.После это- го программа переходит к выполнению бесконечного цикла, в котором через RS232 передаётся значение измеренной температуры. При этом значение температуры считывается из датчика LM75 с помощью функции lm75_temperature_10. Получен- ная информация обрабатывается и передаётся через RS232 с помощью функции printf. Затем делается задержка в 200 мс, и цикл повторяется сначала. При отсутствии отладочной платы STK500 можно самостоятельно реализо- вать этот проект по схеме, приведённой на Рис. 7.35. Цепь питания, сброса и кварцевый резонатор микроконтроллера не показаны (см. Рис. 4.2). DD1 AT90S8515 XTAL1 XTAL2 RESET РВО/ТО РВ1/Т1 PB2/AIN0 PB3/AIN1 PB4/SS PB5/MOSI PB6/MISO PB7/SCK PDO/RXD PD1/TXD PD2/INT0 PD3/IMT1 PO4 PD5/OC1A PD6/WR PD7/RD PA0/AD0 PA1/AD1 PA2/AD2 PA3/AD3 PA4/AD4 PA5/AD5 PA6/AD6 PA7/AD7 РС0/А8 РС1/А9 РС2/А10 РСЗ/А11 РС4/А12 РС5/А13 РС6/А14 РС7/А15 R1 33 32 39 38 37 36 21 22 23 24 25 26 27 vcc зо 31 ALE ОС1В ICP vcc Puc. 7.35. Проект «Therm75». Схема электрическая принципиальная. После подключения собранной схемы к свободному COM-порту следует включить компьютер, а ЗАТЕМ подать питание на схему. Для того чтобы увидеть результаты работы программы, следует запустить CodeVisionAVR (см. Запуск CodeVisionAVR) и загрузить данный проект (см. Коман-
7.11. Проект « Therm 75» 539 да File -> Open (Файл -> Открыть)). Затем следует выполнить настройку термина- ла в соответствии с проектом (см. Команда Settings -» Terminal (Настройки —> Тер- минал)). Пример настройки терминала для данного проекта при подключении к порту СОМ1 компьютера показан на Рис. 7.36. Рис. 7.36. Пример настройки терминала для проекта «Therm75». При другой частоте кварца микроконтроллера или при использовании другой скорости передачи следует изменить настройки UART микроконтроллера (см. строку 50 программы) в соответствии с описанием (datasheet) от Atmel на ис- пользуемый микроконтроллер (см. Команда Help AVR Data Sheets (Помощь Описания AVR)). Для изменения настроек UART можно также воспользоваться Автоматичес- ким программным генератором CodeWizardAVR (см. Автоматический програм- мный генератор CodeWizardAVR). Главное, чтобы скорость передачи UART микроконтроллера совпадала со ско- ростью приёма терминала. После настройки терминала следует записать программу в микроконтроллер (см. Запись программы в чип AVR) и запустить терминал (см. Команда Tools —> Terminal (Инструменты —> Терминал)). При этом в окне терминала можно наблю- дать информацию, которую передаёт в компьютер микроконтроллер AVR (Рис. 7.37). Рис. 7.37. Окно Terminal (Терминал) в режиме отображения символов в виде ASCII с полученной информацией при реализации проекта «Therm75».
540 Глава 7. Примеры проектов Следует отметить, что разные системы и программы используют различные кодировки символов, так что вид информации в окне Terminal (Терминал) может отличаться. Увидеть кодировки, используемые вашей системой можно, перейдя по адресу: Пуск -> Все программы -> Стандартные -» Служебные -» Таблица символов. В появившемся окне Таблица символов (Рис. 7.38) в выпадающем меню в окошке Набор символов можно выбрать требуемую кодировку, а в окошке Шрифт — требуемый шрифт. В CodeVisionAVR по умолчанию установлен шрифт Courier New (см. Команда Settings Editor (Настройки -> Редактор)). Можно выбрать любой символ, просто щёлкнув по нему. Код выбранного символа отображается внизу окна. Терминал CodeVisionAVR в режиме ASCII отображает только символы с кода- ми 0x21...0х7Е, т. е. те символы, которые совпадают практически во всех распро- страненных кодировках (Рис. 7.38).
7.11. Проект « Therm 75» 541 б) Рис. 7.38. Окно Таблица символов. Различная кодировка символов: DOS: США (а) и Windows: кириллица (5). По этой причине в окне Terminal (Терминал) (Рис. 7.37) не видно символа с кодом 0xf8 перед символом «С» (см. строку 71 программы). Для того чтобы убедиться, что символ с кодом 0xf8 принимается, следует щёл- кнуть по кнопке Hex (Шестнадцатеричный) (Рис. 7.39), чтобы перевести терми- нал из режима отображения символов в виде ASCII в режим отображения симво- лов в шестнадцатеричном виде. П Terminal Рис. 7.39. Кнопка Hex (Шестнадцатеричный) в окне Terminal (Терминал). При этом в окне терминала можно наблюдать информацию, которую переда- ёт в компьютер микроконтроллер AVR, в виде шестнадцатеричных кодов переда- ваемых символов (Рис. 7.40).
542 Глава 7. Промеры проектов Рис. 7.40. Окно Terminal (Терминал) в режиме отображения символов в шестнадцатеричном виде с полученной информацией при реализации проекта «Therm75». В окне Terminal (Терминал) периодически повторяется последовательность, состоящая из кодов символов: «74 3D 2В 32 32 2Е 36 F8 43 0D ОА». Код 0x74 соот- ветствует символу «t», код 0x3D — символу «=» и т. д. (см. окно Таблица символов, Рис. 7.38). Видно, что перед символом «С» (код 43) принимается символ с кодом 0xF8, т. е. программа работает правильно. Код OxOD соответствует служебному символу возврата каретки «\г», а код ОхОА — служебному символу перевода стро- ки «\п». Информацию, принимаемую компьютером от микроконтроллерного уст- ройства, можно сохранить в файл. Для этого следует щёлкнуть по кнопке Rx File (Принять в файл) (Рис. 7.41) и в появившемся диалоговом окне Save received characters to a file (Сохранять полученные символы в файл) задать имя, выбрать расширение файла и щёлкнуть по кнопке Сохранить (Рис. 7.42). г. Кнопка Rx File (Принять в файл) в окне Terminal (Терминал). Рис. 7.42. Диалоговое окно Save received characters to a file (Сохранять полученные символы в файл).
7. /1. Проект « Therm 75» 543 Чтобы остановить сохранение получаемой информации в файл, следует щёл- кнуть по кнопке Rx Stop (Остановить приём) (Рис. 7.43). Д Terminal I Disconnect | Hex Code: | Send(J Bx Stop [File [ Hex [ Clear | Д? Rgset Chip Д t=+22.6C r.._. L ................... Puc. 7.43. Кнопка Rx Stop (Остановить приём) в окне Terminal (Терминал). Содержимое этого файла можно просмотреть. Для этого в данном примере лучше всего воспользоваться внутренним просмотровщиком распространённого файлового менеджера Total Commander (сайт программы: http://www.ghisler.com/). Для просмотра содержимого файла, в который была сохранена информация, принимаемая компьютером от микроконтроллерного устройства, следует щёлк- нуть по названию соответствующего файла на панели Total Commander, т. е. вы- брать файл и затем щёлкнуть по кнопке «F3 Просмотр» в нижней части окна (Рис. 7.44). Рис. 7.44. Окно файлового менеджера Total Commander. При этом откроется окно просмотровщика Lister с содержимым выбранного файла. Если в меню Вид выбрана кодировка ASCII (кодировка DOS), то строка символов, полученная компьютером от микропроцессорного устройства, будет выглядеть, как показано на Рис. 7.45.
544 Глава 7. Примеры проектов В Lister - [D:\AVRTools\CodeVision_1 _24_1c\Examples\Iheпп/')\1 henn/’э. txtj - П X Файл Правка t-*22.6°C t=*22.6°C t-*22.6°C t-*22.6°C t-*22.6°c t-*22.6°C t-*22.6°C t-*22.6°C t-*22.6°C t-*22.6°C t-*22.6°C t-*22.6°C t-*22.6°C t-*22.6°C t-*22.6°C t-*22.6°C t-*22.6°C t-*22.6°C t»+22.6°C Вид Справка Только текст 1 Двоичный (фиксированная длина строки) 2 Шестнадцатеричный 3 Г рафика / Мультимедиа / 15-плагины HTML (без показа тегов) Unicode UTF-8 ✓ ASCII (кодировка DOS) зователя ✓ Переносить строки ✓ Показывать текстовый курсор w F6 52% 2 3 5 6 7 5 6 А S V Все изображения в размер # на F Только большие изображения ь размер окна L Изображения по центру окна С Настройка... Запомнить позицию Рис. 7.45. Окно просмотровщика Lister с содержимым выбранного файла в кодировке ASCII (кодировка DOS). Это объясняется тем, что в выбранной кодировке символ с кодом 0xf8 перед символом «С» (см. строку 71 программы) интерпретируется как символ градуса «°» (Рис. 7.46). Рис. 7.46. В кодировке DOS коду 0xf8 соответствует символ градуса «°».
7. /1. Проект « Therm 75» 545 Если в меню Вид выбрать кодировку ANSI (кодировка Windows), то строка сим- волов, полученная компьютером от микропроцессорного устройства, будет вы- глядеть, как показано на Рис. 7.47. ft Lister - [D:\AVRToob\CodeVision_1_24jlc\Examples\Therm75\ThefTn75.txt] Вид Файл Правка t-*22.6iuC t»*22.6wC te*22.6iuC t-*22.6uiC t-*22.6mC t-*22.6ujC t->22.6ujC t-*22.6iuC t-+22.6iuC t“*22.6iuC 22.6uiC 22.6ШС 22.6U1C 22.6U1C 2 3 5 6 7 I Справка Только текст Двоичный (фиксированная длина строки) 2 Шестнадцатеричный Г рафика / Мультимедиа / LS-плагины HTML (без показа тегов) Unicode UTF-8_______ у ANSI (кодировка Windows) Шрифт (кодировка) пользователя ✓ Переносить строки ✓ Показывать текстовый курсор Все изображения в размер окна Только большие изображения в размер окна L Изображения по центру окна Настройка... Запомнить позицию Рис. 7.47. Окно просмотровщика Lister с содержимым выбранного файла в кодировке ANSI (кодировка Windows). Это объясняется тем, что в выбранной кодировке символ с кодом Охй перед сим- волом «С» (см. строку 71 программы) интерпретируется как символ «ш» (Рис. 7.48). Рис. 7.48. В кодировке Windows: кириллица коду 0xf8 соответствует символ «ш».
546 Глава 7. Примеры проектов В этой кодировке символу градуса «°» соответствует код ОхЬО (Рис. 7.49). Рис. 7.49. В кодировке Windows: кириллица символу градуса «°» соответствует код ОхЬО. То есть для того чтобы программы, работающие с кодировкой Windows: кирил- лица, интерпретировали символ, принимаемый перед символом «С» (см. строку 71 программы) как символ градуса «°», следует в строке 71 программы заменить код 0xf8 на ОхЬО: 71: printf("t=%c%i.%u\xbOC\r\n", sign,temp/10, temp%10); Тогда после перекомпиляции и записи программы в микроконтроллер, запус- ка терминала и сохранения в файл информации, принимаемой компьютером от микроконтроллерного устройства, содержимое этого файла в окне просмотров- щика Lister с кодировкой ANSI (кодировка Windows) будет выглядеть, как показа- но на Рис. 7.50. Разумеется, что изменится и вид строки символов в кодировке ASCII (коди- ровка DOS) (Рис. 7.51), т. к. в этой кодировке коду ОхЬО соответствует символ (Рис. 7.52).
7.11. Проект « Therm 75» 547 Ж Lister [D:\AVRTools\CodeVision_1 _24_1c\Exdmptes\Therm75\Therni75.txt] |- |fP||X| Вид Файл Правка t=*22.6°C t-*22.6°C t-*22.6°C t=+22.6°C t=*22.6°C t-+22.6°C t=*22.6°C t=+22.6°C t-*22.6°C t-*22.6°C t=*22.6°C 22.6°C 22.6°C 22.6°C 2 3 4 5 6 7 Справка V ANSI (кодировка Windows) Шрифт (кодировка) пользователя W F6 <✓ Переносить строки Показывать текстовый курсор Бее изображения в размер окна F Только большие изображения в размер огнэ L Изображения по центру окна С Только текст Двоичный (фиксированная длина строки) 2 Шестнадцатеричный 3 Г рафика / Мультимедиа / LS-плагины 4 HTML (без показа тегов) 5 Unicode UTF-8________ Настройка... Запомнить позицию Puc. 7.50. Окно просмотровщика Lister в кодировке ANSI (кодировка Windows) при изменённой программе проекта. Ж Lister [D:\AVRTools\CodeVision_1_24_1c\Examples\Therni75\Therm75.txt] |» |[П || X | Вид Файл Правка t-*22.6^C t=+22.6gC t=*22.6|c t-*22.6|C t-*22.6|C t-*22.6|C t-»22.61c t-*22.6|C t-*22.6gC t-+22.6|c t—22.6|0 t-*22.6|c t-*22.6gc t-+22.6|C t-*22.6gc t-*22.6|C t=*22.6|c t=*22.6& t-+22.6iC 10 Справка Только текст 1 Двоичный (фиксированная длина строки) 2 Шестнадцатеричный 3 Г рафика / Мультимедиа / LS-плагины HTML (без показа тегов) Unicode UTF-8 100 Vo Переносить строки v* Показывать текстовый курсор льзователя Настройка... Запомнить позицию 2 3 4 5 б 7 W F6 Бее изображения в размер ежна F Только большие изображения в размер окна L Изображения по центру окна Рис. 7.51. Окно просмотровщика Lister в кодировке ASCII (кодировка DOS) при изменённой программе проекта.
548 Глава 7. Примеры проектов Рис. 7.52. В кодировке Windows: кириллица коду ОхЬО соответствует символ Таким образом, при написании программы, связанной с передачей данных, следует учитывать кодировку символов, с которой работает та или иная програм- ма, которая обрабатывает данные, получаемые от микроконтроллерного устройс- тва. 7.12. Проект «SPI» Этот пример демонстрирует цифровой вольтметр, использующий шину SPI, реализованный на аналого-цифровом преобразователе AD7896 от Analog Devices. Информация выводится на алфавитно-цифровой LCD-модуль 2x16. В этом проекте содержится один исходный файл — ad7896.c. Текст этого фай- ла с русифицированными комментариями: 2: Цифровой вольтметр, использующий 3: АЦП AD7896 от Analog Devices, 4: подключённый к AT90S8515, 5 : использующий шину SPI 6:
7.12. Проект «SPI» 549 7: CodeVisionAVR C Compiler 8: (С) 2000-2002 HP InfoTech S.R.L. 9: www.hpinfotech.ro 10: 11: Чип: AT90S8515 12: Модель памяти: SMALL 13: Размер стека данных: 128 Б 14: Тактовая частота: 3.6864 МГц 15: 16: подключение AD7896 к STK500 17: [AD7896] [PORTB header] 18: 1 Vin 19: 2 Vref=5V - 10 VTG 20: 3 AGND - 9 GND 21: 4 SCLK - 8 SCK 22: 5 SDATA - 7 MISO 23: 6 DGND - 9 GND 24: 7 CONVST - 2 PB1 25: 8 BUSY - 1 PB0 26: 27: Используется алфавитно-цифровой LCD 2x16, подключённый 28: к разъёму PORTC STK500 следующим образом: 29: 30: [LCD] [Разъём PORTC STK500] 31 : 1 GND - 9 GND 32: 2 +5V - 10 VCC 33: 3 VLC - управление контрастностью LCD напряжение 0..IV 34: 4 RS - 1 PCO 35: 5 RD - 2 PCI 36: 6 EN - 3 PC2 37: 11 D4 - 5 PC4 38: 12 D5 - 6 PC5 39: 13 D6 - 7 PC6 40: 14 D7 - 8 PC7 41: 42: ПРИМЕЧАНИЕ: ПОСЛЕ ПРОГРАММИРОВАНИЯ ЧИПА ОТСОЕДИНИТЕ 43 : 6-ЖИЛЬНЫЙ ПРОГРАММИРУЮЩИЙ КАБЕЛЬ ОТ РАЗЪЁМА SPROG3 44: */ 45: 46: #asm 47: .equ lcd_port=0xl5 48: ttendasm 49: 50: tfinclude <lcd.h> // драйверные программы LCD 51: #include <spi.h> // драйверные программы SPI 52: #include <90s8515.h> 53: #include <stdio.h> 54: ttinclude <delay.h> 55: 56: // опорное напряжение AD7896 [мВ]
550 Глава 7. Примеры проектов 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: #define VREF 5000L // распределение битов ПОРТА В для управляющих сигналов AD7896 #define ADC_BUSY PINB.O ttdefine NCONVST PORTB.1 // буфер LCD-дисплея char lcd.buffer[33]; unsigned read_adc(void) { unsigned result; // начало преобразования в режиме 1 // (высокое разрешение) NCONVST = 0; NCONVST = 1; // ждём окончания преобразования while (ADC-BUSY); // читаем MSB, используя SPI result = (unsigned) spi(0)<<8; // читаем LSB, используя SPI, и соединяем c MSB result I = spi(0); // вычисляем напряжение в мегабайтах result = (unsigned) (((unsigned long) result*VREF)/4096L); // возвращаем измеренное напряжение return result; ) void main(void) { // инициализируем ПОРТ В И РВ.О вход для сигнала BUSY AD7896 II РВ.1 выход для сигнала /CONVST AD7896 и РВ.2 и РВ.3 входы и РВ.4 выход (вывод /SS SPI) II РВ.5 вход II РВ.6 вход (SPI MISO) II РВ.7 выход для сигнала SCLK AD7896 DDRB=0x92; // инициализируем SPI в режиме мастера (master) // прерываний нет, MSB первый, фаза тактирующих импульсов и отрицательная // SCK в режиме ожидания имеет низкий уровень, фаза тактирующих // импульсов = 0 // SCK fxtal/4 SPCR = 0x54; // AD7896 будет работать в режиме 1 // (высокое разрешение) // /CONVST=1, SCLK=0 PORTB = 2; // инициализируем LCD
7.12. Проект «SPI» 551 107: lcd_init(16); 108: lcd_putsf("AD7896 SPI bus\nVoltmeter"); 109: delay_ms(2000); 110: lcd_clear(); 111: // читаем и отображаем входное напряжение АЦП 112: while (1) 113: { 114: sprintf(lcd_buffer,"Uadc=%4umV",read_adc()); 115: lcd_clear(); 116: lcd_puts(lcd_buffer); 117: delay_ms(100); 118: }; 119: } В этом файле знаками /* (начало) и */ (конец) выделены блоки с коммента- риями, а знаком // — строки с комментариями (см. Комментарии). В блоке с комментариями (строки 1...44) даются общая информация о проек- те и рекомендации по подключению АЦП AD7896 и LCD к плате STK500. В строках 46...48 объявляется, какой порт микроконтроллера будет использо- ваться для подключения LCD-модуля. Для этого в программу включён ассемб- лерный код. В строке 46 директива #asm говорит компилятору о начале ассемб- лерного кода, а в строке 48 директива #endasm — о его завершении (см. Директи- вы #asm и #endasm). В строке 47 директива ассемблера .equ присваивает значение идентификатору ___lcd_port. Это значение, соответствующее адресу регистра PORTx выбранного порта (в данном случае выбран Порт С), находим в файле 90s8515.h в соответствующей строке: «sfrb PORTC=Oxl5;». При трансляции ас- семблерного кода, полученного при компиляции данного проекта компилятором Си CodeVisionAVR, ассемблер вместо этого идентификатора подставит его значе- ние. В случае модификации программы при использовании другого порта для под- ключения LCD-модуля достаточно будет лишь заменить значение в указанной директиве .equ, не изменяя остального текста программы. Подробнее о директивах ассемблера см. систему помощи бесплатной про- граммы AVR Studio от Atmel, которую можно скачать на сайте разработчиков мик- роконтроллеров AVR http://www.atmel.com. Директивами #include (строки 50...54) подключаются: функции LCD — файл led.h (см. LCD-функции для дисплеев до 2x40 символов); SPI функции — файл spi.h (см. Функции SPI); заголовочный файл для используемого микроконтроллера AVR (AT90S8515) — файл 90s8515.h, функции ввода/вывода — файл stdio.h (см. Стан- дартные функции ввода/вывода) ; функции задержки — файл delay.h (см. Функции за- держки). Перед компиляцией препроцессор компилятора вставит вместо этих строк текст соответствующих файлов (ом. Директива ^include). Директивами #define (строки 57, 59 и 60) определяются идентификаторы VREF, ADC_BUSY и NCONVST. После этих трёх строк перед компиляцией препроцессор компилятора заменит в тексте программы VREF на 5000L, ADC_BUSY на PINB.0 и NCONVST на PORTB. 1. В случае модификации программы для другого значения опорного напряжения АЦП и других выводов микроконтроллера для сигналов
552 Глава 7. Примеры проектов BUSY и /CONVST АЦП достаточно будет лишь заменить значения в указанных директивах #define, не изменяя остального текста программы (см. Директива # define, tiundef). Директива #define очень удобна, но вовсе не обязательна. В строке 63 объявляется глобальный символьный массив (строковая перемен- ная) Icdjbuffer, состоящий из 33 членов. Этот массив будет расположен в SRAM микроконтроллера. При этом все элементы этого массива автоматически иници- ализируются со значением 0 (см. Массивы). В этом массиве будет храниться ин- формация, предназначенная для вывода на LCD. Так как в настройках проекта на закладке С Compiler (Компилятор Си) выбра- на опция char is unsigned (байт является беззнаковым) (Рис. 7.53), то все данные типа char (байт) компилятор будет обрабатывать как данные типа unsigned char (беззнаковый байт) (см. Закладка С Compiler (Компилятор Си)). То есть массив Icdjbuffer будет состоять из данных типа unsigned char (беззнаковый байт). Рис. 7.53. Опция char is unsigned (байт является беззнаковым) выбрана. Настоятельно рекомендуется явно определять тип данных в тексте про- граммы, т. к. при распечатке текста программы настроек проекта не видно. Это может вызвать неправильное понимание работы программы! Строки 65...82 — это определение функции read_adc. В строке 65 осуществляется объявление функции read_adc. Определитель unsigned (подразумевается unsigned int (беззнаковое целое) (см. Типы данных)) задаёт
7.12. Проект «SPI» 553 тип значения, которое возвращает функция. Далее идёт имя функции read_adc (мо- жет быть произвольным). По этому имени далее в программе осуществляется вы- зов данной функции. Ключевое слово void в скобках указывает на то, что в эту фун- кцию не передаются никакие параметры (см. Функции). В строке 66 открывающая фигурная скобка указывает на начало тела функции read_adc. В строке 67 объявляется локальная переменная result типа unsigned int (беззна- ковое целое). Ключевое слово unsigned в скобках означает unsigned int (см. Типы данных). В строках 70, 71 перед компиляцией препроцессор заменит идентификатор NCONVST на PORTB. 1 (см. строку 60). Таким образом, в бит 1 регистра PORTx Порта В (см. Доступ к регистрам ввода/вывода) сначала будет записана 1 (строка 70), а затем 0 (строка 71), т. е. на выводе 1 Порта В будет сформирован короткий отрицательный импульс, который осуществит запуск процесса преобразования АЦП. В строке 73 реализован цикл с помощью оператора while (см. Оператор while). Этот оператор будет выполняться до тех пор, пока выражение в скобках истинно, т. е. не 0. Перед компиляцией препроцессор заменит идентификатор ADC BUSY на PINB.0 (см. строку 59). PINB.0 — это значение, считанное из бита 0 регистра PINx Порта В (см. Доступ к регистрам ввода/вывода), т. е. значение логического уровня, который к настоящему времени присутствует на физическом (т. е. реаль- ном) выводе 0 Порта В. Таким образом, оператор while будет выполняться до тех пор, пока на выводе 0 Порта В не появится уровень логического 0, т. е. пока не бу- дет закончено преобразование АЦП. В строке 75 с помощью функции spi (см. Функции SPI) считывается старший байт (MSB) результата преобразования АЦП (согласно конфигурации регистра SPCR в строке 101, см. ниже). После этого согласно приоритетам операций (см. Приоритеты операций и порядок вычислений) выполняется преобразование типов (приоритет 2). Беззнаковый байт (unsigned char) 0b ХХХХХХХХ, который возвраща- ет функция spi, преобразовывается в беззнаковое целое 0Ь 0000 0000 ХХХХ ХХХХ (ключевое слово unsigned в скобках означает unsigned int, см. Типы данных). После преобразования типов полученное значение сдвигается на 8 битов влево (знак опе- рации «, приоритет 5), т. е. младший байт полученного значения перемещается в старший байт, а младший байт заполняется нулями — 0Ь ХХХХ ХХХХ 0000 0000. Ре- зультирующее значение присваивается переменной result. В строке 77 с помощью функции spi считывается младший байт (LSB) резуль- тата преобразования АЦП. После этого осуществляется операция побитного ИЛИ с присваиванием (знак операции |=) текущего значения переменной result (0b ХХХХ ХХХХ 0000 0000) и значения, возвращённого функцией spi (0b YYYY YYYY). При этом каждый бит первого операнда сравнивается с соответствующим битом второго операнда. Если любой (или оба) из сравниваемых битов равен 1, то соответствующий бит результата устанавливается в 1, в противном случае — в 0 (см. Бинарные операции). Полученное значение (0Ь ХХХХ ХХХХ YYYY YYYY) при- сваивается переменной result. Таким образом, в переменной result оказывается результат преобразования АЦП. Функция spi определена в подключённом файле spi.h (строка 51).
554 Глава 7. Примеры проектов В строке 79 по полученному результату преобразования АЦП вычисляется значение напряжения в мегабайтах. Перед компиляцией препроцессор заменит идентификатор VREF на 5000L (см. строку 57). Буква L в конце числа означает, что эта константа имеет тип long integer (длинное целое) (см. Константы). Сначала согласно приоритетам операций (см. Приоритеты операций и порядок вычислений) вычисляется выражение в скобках (приоритет 1). В скобках первым выполняется преобразование типов (приоритет 2). Переменная result типа unsigned int (беззна- ковое целое) преобразовывается в переменную типа unsigned long int (беззнаковое длинное целое). Ключевые слова unsigned long в скобках означают unsigned long int (см. Типы данных). Затем выполняется операция умножения (знак операции *, приоритет 3) значения переменной result на 5000L. После этого выполняется опе- рация деления (знак операции /, приоритет 3) полученного значения на 4096L. Все вычисления ведутся с длинными целыми, чтобы не потерять значащие циф- ры. В заключение результат всех операций приводится к типу unsigned int (беззна- ковое целое) и его значение присваивается переменной result. Ключевое слово unsigned в скобках означает unsigned int (см. Типы данных). Таким образом, по окон- чании выполнения функции read adc в переменной result хранится значение входного напряжения в мегабайтах. В строке 81 находится оператор return, который завершает выполнение функ- ции, в которой он задан, и возвращает управление в вызывающую функцию. Зна- чение выражения, стоящего за оператором return, возвращается в вызывающую функцию в качестве значения вызываемой функции (см. Оператор return). Таким образом, функция read_adc запускает процесс преобразования АЦП, дожидается его окончания, считывает результат преобразования АЦП, по его зна- чению рассчитывает входное напряжение АЦП и возвращает значение локальной переменной result, т. е. значение входного напряжения АЦП в мегабайтах. В строке 82 закрывающая фигурная скобка указывает конец тела функции read_adc. Строки 84...119 — это определение основной функции программы (main). Эта функция обязательно должна присутствовать во всех программах (но только ОДНА!). Именно с этой функции, в каком бы месте программы она ни находи- лась, начинается выполнение программы. В строке 84 осуществляется объявление функции main. Вначале стоит ключе- вое слово void, указывающее на то, что эта функция не возвращает никаких зна- чений. Затем следует имя функции main (ЕГО ИЗМЕНЯТЬ НЕЛЬЗЯ!) и ключевое слово void в скобках, которое указывает на то, что в эту функцию не передаются никакие параметры (см. Функции). В строке 85 открывающая фигурная скобка указывает начало тела функции main. В строке 94 в регистр DDRx Порта В записывается значение 0x92 = 0Ь 1001 0010, тем самым выводы 0, 2, 3, 5 и 6 Порта В делаются входами, а 1,4 и 7 — выхо- дами (см. Доступ к регистрам ввода/вывода). В строке 101 осуществляется инициализация SPI. При этом в регистр SPCR (Регистр управления SPI) записывается значение 0x54 = 0Ь 0101 0100, тем самым весь обмен данными SPI конфигурируется так, как описано в комментариях (строки 95...100). Подробнее см. Функции SPI.
7.12. Проект «SPI» 555 В строке 105 в регистр PORTx Порта В записывается значение 2 = 0Ь 0000 0010, тем самым первоначально значение сигнала /CONVST АЦП устанавливает- ся в 1, а значение сигнала SCLK — в 0. АЦП будет работать в режиме 1 (режим высокого разрешения). Подробнее см. описание микросхемы AD7896, которое можно найти на сайте производителя: http://www.analog.com/. В строке 107 осуществляется вызов функции Icdjnit с фактическим парамет- ром 16 (количество столбцов в используемом модуле LCD). Эта функция инициа- лизирует модуль LCD, очищает дисплей и устанавливает позицию для вывода символа в ряд 0 столбца 0. Курсор не отображается. В строке 108 осуществляется вызов функции led putsf, которая отображает в текущей позиции дисплея последовательность «AD7896 SPI bus\nVoltmeter», т. е. сначала в верхней строке LCD будет выведена последовательность «AD7896 SPI bus», затем курсор будет переведён на нижнюю строку (последовательность \п), в которой будет выведена последовательность «Voltmeter». Эти функции определены в подключённом файле led.h (строка 50). Подробнее см. LCD-функции для дисплеев до 2x40 символов. В строке 109 функция delay_ms генерирует задержку 2000 мс (2 с) (см. Функции задержки). Эта функция определена в подключённом файле delay.h (строка 54). В строке ПО осуществляется вызов функции lcd_clear, которая очищает LCD и устанавливает позицию для вывода символа в ряд 0 столбца 0. Эта функция оп- ределена в подключённом файле led.h (строка 50). В строке 112 реализован бесконечный цикл с помощью оператора while (см. Оператор while). Этот оператор будет выполняться до тех пор, пока выражение в скобках истинно, т. е. не 0. Выражение в скобках — 1 (можно также написать 5 или 89), а не 0, т. е. всегда истинно. Следовательно, этот оператор будет выпол- няться бесконечно. В этом цикле бесконечно будет выполняться группа операторов, заключённая в фигурные скобки, находящиеся в строках 113 (открывающая скобка) и 118 (за- крывающая скобка). В строке 114 функция sprintf осуществляет форматные преобразования и по- мещает результаты в строку Icdjbuffer. Спецификация преобразования «Uadc = %4umV» означает, что сначала будет выведена последовательность «Uadc =», затем будет вызвана функция read_adc, и значение, которое она вернёт, будет выведено в поле шириной, по крайней мере, в четыре символа (описатель width — 4), в формате десятичного целого беззнакового числа (символ преобразо- вания 'и'). Если значение имеет менее четырёх цифр, то оно будет выровнено по правому краю (флаг отсутствует), а левая часть будет заполнена пробелами (т. к. в описателе width в начале отсутствует 0). В заключение будет выведена последова- тельность «mV». Функция sprintf определена в подключённом файле stdio.h (стро- ка 53) (см. Стандартные функции ввода/вывода). В строке 115 снова осуществляется вызов функции lcd_clear, которая очищает LCD и устанавливает позицию для вывода символа в ряд 0 столбца 0. В строке 116 функция lcd_puts отображает в текущей дисплейной позиции со- держимое строки Icdjbuffer, которая была сформирована в 114-й строке программы. Эти функции определены в подключённом файле led.h (строка 50).
556 Глава 7. Примеры проектов В строке 117 функция delay_ms генерирует задержку 100 мс. Эта функция оп- ределена в подключённом файле delay.h (строка 54). Программа работает следующим образом. После подачи питания (или аппарат- ного сброса на выводе RESET микроконтроллера) начинает выполняться функция main. После инициализации периферийных устройств (портов, регистров и SPI) последовательно вызываются функция Icdjnit, которая инициализирует LCD-мо- дуль, и функция lcd_putsf, которая в верхнюю строку LCD-модуля выводит после- довательность «AD7896 SPI bus», а в нижнюю — «Voltmeter» (Рис. 7.54). DD2 AT90S8515 XTAL1 XTAL2 РВОДО РВ1Д1 P82,AJN0 P83/AIN1 PB4/SS PB5/MOSI PB6/MISO PB7/SCK POO/RXD РО1ДХО PD2/INT0 PD3/INT1 РОД PD5/OCIA PD6/WR PO7/RO PAO/ADO РА1/АО1 PA2/AD2 PA3,AD3 PA4/AD4 PA5/AD5 РА6/АО6 РА7/АО7 рсо/ла РС1/А9 РС2/А10 РСЗ/А11 РС4/А12 РС5/А13 РС6/А14 РС7/А15 LCD1 8о 8 83 88 о RD7896 SPI bus Uoltneter GND 25 RV2 10k ОС1В ЮР Рис. 7.54. Результат работы программы — вывод последовательности «AD7896 SPI bus\n Voltmeter». После задержки в 2000 мс дисплей очищается. После этого программа переходит в бесконечный цикл отображения значения измеренного входного напряжения АЦП. При этом вызывается функция read_adc и значение, которое она возвращает (значение входного напряжения АЦП в мега- байтах), отображается на LCD после надписи «Uadc=». В конце выводятся едини- цы измерения «mV». Затем делается задержка в 100 мс, и цикл повторяется сначала. Результат работы программы показан на Рис. 7.55. DD2 AT90S8515 РВО/ТО РВ1/Т1 PB2/AIN0 PB3/AJN1 РВ4SS PB5/MOSI PB6/MISO PB7/SCK РАО/АОО РА1/АО1 PA2/AD2 РАЗ/АОЗ PA4/AD4 PA5/AD5 PA6/AD6 РА7/АО7 PD0/RXD РО1/ТХО PO2/INT0 PO3/INT1 РОД PO5/QC1A PO6WR РО7/ЙО РС0/А8 РС1/А9 РС2/А10 РСЗ/А11 РС4/А12 РС5/А13 РС6/А14 РС7/А15 LCD1 Рис. 7.55. Результат работы программы — отображения значения измеренного входного напряжения АЦП.
7.13. Проект «Мах1241» 557 В данном проекте не является необходимым вызов функции lcd_clear в строке 110, достаточно того, что в бесконечном цикле эта функция вызывается в строке 115. При отсутствии отладочной платы STK500 можно самостоятельно реализо- вать этот проект по приведённой схеме. Цепь питания, сброса и кварцевый резо- натор микроконтроллера не показаны (см. Рис. 4.2). 7.13. Проект «Мах1241» Этот пример демонстрирует цифровой вольтметр, использующий шину SPI, реализованный на микросхеме МАХ1241. Значение измеренного напряжения пе- редаётся через интерфейс RS232. Микросхема МАХ1241 фирмы Maxim Integrated Products (Рис. 7.56) представ- ляет собой 12-битный аналого-цифровой преобразователь (АЦП). Описание этой микросхемы можно найти на сайте производителя: http://www.maxim-ic.com. а) VDDQ AIN С SHDNQ REFC N) б) JSCLK 7 3CS 6 JDOUT 5 3GND 1 8 3 4 Рис. 7.56. Микросхема МАХ1241 — внешний вид (а) и назначение выводов (6). Пример подключения МАХ1241 к микроконтроллеру AT90S8515 с использо- ванием шины SPI показан на Рис. 7.57. Цепь питания, сброса и кварцевый резо- натор микроконтроллера не показаны (см. Рис. 4.2). VREF vcc DD2 AT90S8515 VlN DD1 1 МАХ1241 19 18 9 XTAL1 XTAL2 RESET AIN DOUT SCLK< CS Qr- REF SHDN 0“ GND 10 12 13 14 15 16 PBO/TO PB1/T1 PB2/AIN0 PB3/AIN1 PB4/SS PB5/MOSI PB6/MISO PB7/SCK PAO/ADO PA1/AD1 PA2/AD2 PA3/AD3 PA4/AD4 PA5/AD5 PA6/AD6 PA7/AD7 39 38 37 36 35 33 32 PDO/RXD PD1/TXD PD2/INT0 PD3/INT1 PD4 PD5/0C1A PD6/WR PD7/RD PC0/A8 PC1/A9 PC2/A10 PC3/A11 PC4/A12 PC5/A13 PC6/A14 PC7/A15 21 22 23 24 25 26 27 28 ALE OCIB ICP 30 29 31 2 5 Puc. 7.57. Пример подключения MAX1241 к микроконтроллеру AT90S8515.
558 Глава 7. Примеры проектов В этом проекте содержится один исходный файл — шах1241.с. файла с русифицированными комментариями: Текст этого 1: 2: /* ***********************************^ Цифровой вольтметр, использующий r-k-k-k-k-k-k-k-k-k-k-k-k 3: 4: 5: б: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: АЦП Maxim МАХ1241, подключённый к AT90S8515 по шине SPI Измеренное напряжение передаётся через интерфейс RS232 STK500 Параметры связи: 9600 8N1 CodeVisionAVR С Compiler (С) 2000-2002 HP InfoTech S.R.L. www.hpinfotech.ro Тип чипа: AT90S8515 Тактовая частота: 3.686400 МГц Модель памяти: Small Размер внутреннего RAM: 512 Размер внешнего RAM: 0 Размер стека данных: 128 k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: МАХ1241 подключён к STK500: [МАХ1241] [Разъём PORTB] 1 VDD - 10 +5V 2 Vin 3 /SHDN - 1 PB0 4 REF - 10 +5V 5 GND - 9 GND 6 DOUT - 7 MISO 7 /CS - 2 PB1 8 SCLK - 8 SCK Для того чтобы использовать разъём RS232 SPARE на STK500, должны быть сделаны следующие соединения [разъём RS232 SPARE] [разъём PORTD] RXD - 1 PD0 TXD - 2 PD1 ПРИМЕЧАНИЕ: ПОСЛЕ ПРОГРАММИРОВАНИЯ ЧИПА ОТСОЕДИНИТЕ б-ЖИЛЬНЫЙ ПРОГРАММИРУЮЩИЙ КАБЕЛЬ ОТ РАЗЪЁМА SPROG3 */ ttinclude <90s8515.h> // Стандартные функции ввода/вывода ttinclude <stdio.h> // функции SPI ^include <spi.h>
7.13. Проект «Мах 1241» ъ 559 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: // функции задержки ^include <delay.h> // Опорное напряжение МАХ1241 [мВ] ttdefine VREF 5000 // определения управляющих сигналов МАХ1241 ttdefine NSHDN PORTB.0 ttdefine NCS PORTB.1 tfdefine DOUT PINB.6 union adcu { unsigned char byte[2]; unsigned int word; }; // Сделаем одно АЦ преобразование и вернём значение unsigned int maxl241_read(void) { union adcu adc_data; // вывод MAX1241 из режима останова NSHDN = 1; // ждём 5 мкс, чтобы МАХ1241 проснулся delay_us(5); // теперь выберем чип, чтобы начать преобразование NCS = 0; // ждём завершения преобразования //в течение преобразования DOUT будет 0 while (DOUT==0); // DOUT = 1 -> преобразование завершено // читаем MSB (старший байт) adc_data.byte[1] = spi(0); // читаем LSB (младший байт) adc_data.byte[0 ]= spi(0); // отменим выбор чипа NCS = 1; // введём режим выключения NSHDN = 0; // теперь отформатируем результат и вернём его return (adc_data.word»3) &0xf f f ; } void main(void) { // здесь будет храниться результат преобразования unsigned n;
560 Глава 7. Примеры проектов 90: // Инициализация портов ввода/вывода 91: // Порт А 92: DDRA=0x00; 93: PORTA=0x00; 94: II Порт В 95: // Вывод /SS установим как выход 96: //с уровнем 1, это требует 97: // SPI для работы в режиме master 98: DDRB=0xA3; 99: PORTB=Oxl2; 100: // Порт С 101: DDRC=0x00; 102: PORTC=0x00; 103: // Порт D 104: DDRD=0x00; 105: PORTD=0x00; 106: // Инициализация UART 107: // Параметры связи: 8 данных, 1 стоп, нет чётности 108: // UART приёмник: выкл 109: // UART передатчик: вкл 110: UCR=0x08; 111: // Скорость передачи UART: 9600 0 3.6864 МГц 112: UBRR=23; 113: // Инициализация SPI 114: // Тип SPI: Master 115: // Тактовая частота SPI: 921.6 кГц 116: // Фаза тактирования SPI: половина цикла 117: // Полярность тактирования SPI: низкий уровень 118: // Порядок данных SPI: MSB (старший байт) первый 119: SPCR=0x50; 120: putsf("МАХ1241 Demo using the CodeVisionAVR C Compiler"); 121: putsf (11 ***********************************************\n" ); 122: // Сделаем АЦ преобразования и пошлём результаты на RS232 123: while (1) 124: { 125: n=maxl241_read(); 126: printf("МАХ1241-> N=%4u U=%4umV\r\n",n,(unsigned)((long)n*VREF/4096)); 127: // 0.3 sec. delay 128: delay_ms(300); 129: }; 130: } В этом файле знаками /* (начало) и */ (конец) выделены блоки с коммента- риями, а знаком // — строки с комментариями (см. Комментарии).
7.13. Проект «Мах1241» 561 В блоке с комментариями (строки 1 ...37) даются общая информация о проек- те и рекомендации по подключению АЦП МАХ1241 и использованию разъёма RS232 SPARE на плате STK500. Директивами #include (строки 39...45) подключаются: заголовочный файл для используемого микроконтроллера AVR (AT90S8515) — файл 90s8515.h; функции ввода/вывода — файл stdio.h (см. Стандартные функции ввода/вывода), SPI-фун- кции — файл spi.h (см. Функции SPI) и функции задержки — файл delay.h (см. Функции задержки) соответственно. Перед компиляцией препроцессор компиля- тора вставит вместо этих строк текст соответствующих файлов (см. Директива ^include). Директивами #define (строки 48...52) определяются идентификаторы VREF, NSHDN, NCS и DOUT После этих строк перед компиляцией препроцессор ком- пилятора заменит в тексте программы VREF на 5000, NSHDN на PORTB.O, NCS на PORTB. 1 и DOUT на PINB.6. В случае модификации программы для другого значения опорного напряжения АЦП и других выводов микроконтроллера для сигналов NSHDN, NCS и DOUT АЦП достаточно будет лишь заменить значения в указанных директивах #define, не изменяя остального текста программы (см. Директива #define, #undef). Директива #define очень удобна, но вовсе не обя- зательна. Строки 54...58 — это описание объединения adcu (см. Объединения (смеси)), т. е. фактически определение соответствующего типа данных. В строке 54 осуществляется объявление объединения. На это указывает клю- чевое слово union. В фигурных скобках, расположенных в строках 55 (открывающая скобка) и 58 (закрывающая скобка) объявляются элементы объединения. В строке 56 объяв- ляется массив byte, состоящий из двух членов типа unsigned char (беззнаковый байт). В строке 57 объявляется переменная word типа unsigned int (беззнаковое це- лое). Строки 61...84 — это определение функции maxl241_read. В строке 61 осуществляется объявление функции. Определитель unsigned int (беззнаковое целое) задаёт тип значения, которое возвращает функция (см. Типы данных). Далее идёт имя функции maxl241_read (может быть произвольным). По этому имени далее в программе осуществляется вызов данной функции. Ключе- вое слово void в скобках указывает на то, что в эту функцию не передаются ника- кие параметры (см. Функции). В строке 62 открывающая фигурная скобка указывает на начало тела функции maxl241_read. В строке 63 объявляется локальная переменная adc_data типа union adcu, кото- рый определён в строках 54...58 (см. Типы данных). В строке 65 перед компиляцией препроцессор заменит идентификатор NSHDN на PORTB.O (см. строку 50). Таким образом, в строке 65 в бит 0 регист- ра PORTx Порта В (см. Доступ к регистрам ввода/вывода) будет записана 1, т. е. на выводе 0 Порта В будет установлен высокий уровень, тем самым осуществля- ется вывод АЦП из режима останова. Подробнее см. описание микросхемы МАХ1241, которое можно найти на сайте производителя: http://www.maxim-ic.com.
562 Глава 7. Примеры проектов В строке 67 функция delay_us генерирует задержку 5 мкс (см. Функции задерж- ки). Эта функция определена в подключённом файле delay.h (строка 45). За это время АЦП просыпается, т. е. выходит из режима останова. В строке 69 перед компиляцией препроцессор заменит идентификатор NCS на PORTB.1 (см. строку 51). Таким образом, в строке 69 в бит 1 регистра PORTx Порта В (см. Доступ к регистрам ввода/вывода) будет записан 0, т. е. на выводе 1 Порта В будет установлен низкий уровень, тем самым будет выбрана микросхема АЦП. В строке 72 реализован цикл с помощью оператора while (см. Оператор while). Перед компиляцией препроцессор заменит идентификатор DOUT на PINB.6 (см. строку 52). PINB.6 — это значение, считанное из бита 6 регистра PINx Порта В (см. Доступ к регистрам ввода/вывода), т. е. значение логического уровня, кото- рый к настоящему времени присутствует на физическом (т. е. реальном) выводе 6 Порта В. Этот оператор while будет выполняться до тех пор, пока выражение в скобках истинно, т. е. пока на выводе 6 Порта В значение логического уровня равно 0 (знак операции равенства ==, см. Бинарные операции). Таким образом, оператор while будет выполняться до тех пор, пока на выводе 6 Порта В не появит- ся уровень логической 1, т. е. пока не будет закончено преобразование АЦП. Следует отметить, что при подключении микросхемы МАХ1241 по интерфей- су SPI для получения 12-битного результата преобразования микроконтроллеру требуется последовательно считать из АЦП 2 байта. Старший байт (MSB) содер- жит единицу в самом старшем бите и семь старших битов результата преобразова- ния АЦП. Младший байт (LSB) содержит остальные пять битов результата преоб- разования АЦП и три нуля в самых младших битах (Рис. 7.58). Рис. 7.58. Содержимое байтов данных АЦП МАХ1241 по окончании преобразования. В строке 75 с помощью функции spi из АЦП считывается старший байт (MSB) (согласно конфигурации регистра SPCR в строке 119, см. ниже), и его зна- чение (Ob 1ХХХХХХХ) присваивается второму члену (члену с индексом 1) массива byte объединения adcdata. В строке 77 с помощью функции spi из АЦП считывается младший байт (LSB), и его значение (ОЬ ХХХХ Х000) присваивается первому члену (члену с ин- дексом 0) массива byte объединения adc data. Функция spi определена в подключённом файле spi.h (строка 43). В строке 79 перед компиляцией препроцессор заменит идентификатор NCS на PORTB.1 (см. строку 51). Таким образом, в строке 79 в бит 1 регистра PORTx Порта В (см. Доступ к регистрам ввода/вывода) будет записана единица, т. е. на выводе 1 Порта В будет установлен высокий уровень, тем самым будет отменён выбор микросхемы АЦП.
7.13. Проект «Мах!241» 563 В строке 81 перед компиляцией препроцессор заменит идентификатор NSHDN на PORTB.O (см. строку 50). Таким образом, в строке 81 в бит 0 регистра PORTx Порта В (см. Доступ к регистрам ввода/вывода) будет записан 0, т. е. на выводе 0 Порта В будет установлен низкий уровень, тем самым осуществляется перевод АЦП в режим останова. В строке 83 находится оператор return, который завершает выполнение функ- ции, в которой он задан, и возвращает управление в вызывающую функцию. Зна- чение выражения, стоящего за оператором return, возвращается в вызывающую функцию в качестве значения вызываемой функции (см. Оператор return). Выражение, которое стоит за оператором return, вычисляется в следующем порядке. Сначала согласно приоритетам операций (см. Приоритеты операций и порядок вычислений), выполняется операция в скобках (приоритет 1). Значение элемента word (в котором после выполнения строк 75 и 77 хранится значение 2 байтов, считанных микроконтроллером из АЦП, — Ob 1ХХХ ХХХХ ХХХХ Х000) объединения adc data сдвигается на 3 бита вправо (знак операции — »). Затем выполняется операция побитного И (знак операции &, приоритет 2) значения, полученного после сдвига (ОЬ 0001 ХХХХ ХХХХ ХХХХ), с числом ОхйТ = 0Ь 0000 1111 1111 1111. При этом каждый бит первого операнда сравнивается с соответс- твующим битом второго операнда. Если оба сравниваемых бита 1, то соответству- ющий бит результата устанавливается в 1, в противном случае — в 0 (см. Бинарные операции). В результате получится значение 0Ь 0000 ХХХХХХХХ ХХХХ, т. е. 12-бит- ный результат преобразования АЦП МАХ1241. Таким образом, функция maxl241_read выводит АЦП из режима останова, за- пускает процесс преобразования АЦП, дожидается его завершения, считывает данные из АЦП, выделяет 12-битный результат преобразования и возвращает его значение в вызывающую функцию. В строке 84 закрывающая фигурная скобка указывает на конец тела функции maxl241_read. Строки 86... 130 — это определение основной функции программы (main). Эта функция обязательно должна присутствовать во всех программах (но только ОДНА!). Именно с этой функции, в каком бы месте программы она ни находи- лась, начинается выполнение программы. В строке 86 осуществляется объявление функции main. В начале стоит ключе- вое слово void, указывающее на то, что эта функция не возвращает никаких зна- чений. Затем следует имя функции main (ЕГО ИЗМЕНЯТЬ НЕЛЬЗЯ!) и ключевое слово void в скобках, которое указывает на то, что в эту функцию не передаются никакие параметры (см. Функции). В строке 87 открывающая фигурная скобка указывает на начало тела функции main. В строке 89 объявляется локальная переменная п типа unsigned int (беззнако- вое целое). Ключевое слово unsigned означает unsigned int (см. Типы данных). В строках 92, 93 осуществляется инициализация Порта А. В регистр DDRA записывается значение 0x00 = 0Ь 0000 0000 (строка 91), т. е. все выво- ды порта делаются входами. В регистр PORTA записывается значение 0x00 = 0Ь 0000 0000 (строка 91), т. е. все выводы будут включены без подтяги- вающих резисторов.
564 Глава 7. Примеры проектов В строках 98, 99 осуществляется инициализация Порта В. В регистр DDRB за- писывается значение ОхАЗ = ОЬ 1010 ООН (строка 98), т. е. выводы 0, 1, 5 и 7 порта делаются выходами, а выводы 2, 3, 4 и 6 — входами. В регистр PORTB записыва- ется значение 0x12 = ОЬ 0001 0010 (строка 99), т. е. выводы 0, 5 и 7 будут выхода- ми, на которых присутствует уровень логического 0, вывод 1 — выход с уровнем 1, выводы 2, 3 и 6 — входы без подтягивающих резисторов, вывод 4 — вход с под- тягивающим резистором. В строках 101, 102 и 104, 105 осуществляется инициализация Порта С и Порта D. Оба этих порта конфигурируются точно так же, как и Порт А (строки 92, 93). Подробнее см. Доступ к регистрам ввода/вывода. В строках 110 и 112 осуществляется инициализация асинхронного приёмопе- редатчика UART В строке НО в регистр UCR (регистр управления универсальным асинхрон- ным приёмопередатчиком UART) записывается значение 8, тем самым разреша- ется работа передатчика UART, посылки 8-битовые, 1 столовый бит, без контроля чётности. В строке 112 в регистр UBRR (регистр скорости передачи универсального асинхронного приёмопередатчика UART) записывается значение 23, тем самым задаётся скорость передачи UART При тактовой частоте 3.6864 МГц значению 23 в регистре UBRR соответствует скорость передачи 9600 бод. Подробнее о работе приёмопередатчика UART см. описание (datasheet) от Atmel на используемый микроконтроллер (см. Команда Help -> AVR Data Sheets (Помощь -» Описания AVR)). В строке 119 осуществляется инициализация SPI. При этом в регистр SPCR (Регистр управления SPI) записывается значение 0x50 = 0Ь 0101 0000, тем самым весь обмен данными SPI конфигурируется так, как описано в комментариях (строки 113... 118). Подробнее см. Функции SPI. В строках 120, 121 осуществляется вызов функции putsf, которая выводит че- рез UART последовательности «МАХ1241 Demo using the CodeVisionAVR С И л* л* л* л* л* л* л* л* л* л* л* л* л* л* л* л* л* л* Th л* tr л* tr л* л* tr л* tr л* tr л* 1"^ Л^1 IO щая последовательность \n означает перевод строки. В строке 123 реализован бесконечный цикл с помощью оператора while (см. Оператор while). Этот оператор будет выполняться до тех пор, пока выраже- ние в скобках истинно, т. е. не 0. Выражение в скобках — 1 (можно также напи- сать 17 или 21), а не 0, т. е. всегда истинно. Следовательно, этот оператор будет выполняться бесконечно. В этом цикле бесконечно будет выполняться группа операторов, заключённая в фигурные скобки, находящиеся в строках 124 (открывающая скобка) и 129 (за- крывающая скобка). В строке 125 вызывается функция maxl241_read, и значение, которое она воз- вращает, присваивается переменной п. Таким образом, в переменной п хранится результат преобразования АЦП. В строке 126 функция printf осуществляет форматные преобразования и пере- даёт строку символов через UART (RS232). Спецификация преобразования «МАХ1241-> N = %4u U = %4umV\r\n» означает, что сначала будет передана пос- ледовательность «МАХ1241-> N =», затем будет передано значение переменной п
7.13. Проект «Мах 1241» 565 в поле шириной, по крайней мере, в четыре символа (описатель width = 4), в фор- мате десятичного целого беззнакового числа (символ преобразования и’). Если значение имеет менее 4 цифр, то оно будет выровнено по правому краю (флаг от- сутствует), а левая часть будет заполнена пробелами (т. к. в описателе width в нача- ле отсутствует 0). Затем будет передана последовательность «и=». После этого бу- дет передано значение выражения (unsigned)((long)n*VREF/4096) в том же форма- те, что и значение переменной п. В этом выражении препроцессор перед компиляцией заменит идентификатор VREF на 5000 (см. строку 48). Сначала согласно приоритетам операций (см. При- оритеты операций и порядок вычислений) вычисляется выражение в скобках (при- оритет 1). В скобках первым выполняется преобразование типов (приоритет 2). Переменная п типа unsigned int (беззнаковое целое) преобразовывается в перемен- ную типа long int (длинное целое). Ключевое слово long означает long int (см. Типы данных). Затем слева направо выполняются операция умножения (знак операции *, приоритет 3) значения переменной п на 5000 и операция деления (знак опера- ции /, приоритет 3) полученного значения на 4096. После этого результат всех операций приводится к типу unsigned int (беззнаковое целое). Ключевое слово unsigned в скобках означает unsigned int (см. Типы данных). В заключение передаётся последовательность «mV» и два символа: \г — сим- вол возврата каретки и \п — символ перевода строки. Функции putsf и printf определены в подключённом файле stdio.h (строка 41). Подробнее см. Стандартные функции ввода/вывода. В строке 128 функция delay_ms генерирует задержку 300 мс (см. Функции за- держки). Эта функция определена в подключённом файле delay.h (строка 45). В строке 130 закрывающая фигурная скобка указывает на конец тела функции main. Программа работает следующим образом. После подачи питания (или аппаратного сброса на выводе RESET микроконтроллера) начинает выпол- няться функция main. При этом объявляется переменная п и проводится инициализация периферийных устройств (портов, регистров, UART и SPI). Затем вызываются функции putsf, которые через RS232 передают после- довательности «МАХ1241 Demo using the CodeVisionAVR C Compiler» и <(***********************************************\u^ После этого программа переходит в бесконечный цикл, в котором вызывается функция maxl241_read и по значению, которое она возвращает (12-битный код значения входного напряжения АЦП), рассчитывается значение входного напря- жения АЦП в мегабайтах. Затем через RS232 передаются последовательность «МАХ1241-> N =», значение, которое возвращает функция maxl241_read, после- довательность « U =» и рассчитанное значение входного напряжения АЦП в ме- габайтах. Затем делается задержка в 300 мс, и цикл повторяется сначала.
566 Глава 7. Примеры проектов При отсутствии отладочной платы STK500 можно самостоятельно реализо- вать этот проект по схеме, приведённой на Рис. 7.59. Цепь питания, сброса и кварцевый резонатор микроконтроллера не показаны (см. Рис. 4.2). DD2 vcc vcc AT90S8515 XTAL1 XTAL2 RESET РВО/ТО РВ1/Т1 P82/AJN0 PB3/AIN1 PB4/SS PB5/MOSI PB6/MISO PB7/SCK POO/RXD PD1/TXD PD2/INT0 PD3/INT1 PD4 PD5/OC1A PD6/WR PO7/RD PA0/AD0 РА1/АО1 PA2/AD2 PA3/AD3 PA4/AD4 PA5/AD5 PA6/AD6 PA7/AD7 РС0/А8 РС1/А9 РС2/А10 РСЗ/А11 РС4/А12 РС5/А13 РС6/А14 РС7/А15 39 38 37 36 35 33 21 22 23 24 25 27 VCC ALE ОС1В ICP 30 29 Рис. 7.59. Проект «Мах1241». Схема электрическая принципиальная. После подключения собранной схемы к свободному COM-порту компьютера следует включить компьютер, а затем подать питание на схему. Для того чтобы увидеть результаты работы программы, следует запустить CodeVisionAVR (см. Запуск CodeVisionAVR) и загрузить данный проект (см. Команда File -> Open (Файл -> Открыть)). Затем следует выполнить настрой- ку терминала в соответствии с проектом (см. Команда Setting? -> Terminal (На- стройки -> Терминал)). Пример настройки терминала для данного проекта при подключении к порту СОМ1 компьютера показан на Рис. 7.60.
7.13. Проект «Мах1241» 567 Рис. 7.60. Пример настройки терминала для проекта «Мах1241». При другой частоте кварца микроконтроллера или при использовании другой скорости передачи следует изменить настройки UART микроконтроллера (см. строку 112 программы) в соответствии с описанием (datasheet) от Atmel на ис- пользуемый микроконтроллер (см. Команда Help -> AVR Data Sheets (Помощь —> Описания AVR)). Для изменения настроек UART можно также воспользоваться автоматичес- ким программным генератором CodeWizardAVR (см. Автоматический програм- мный генератор CodeWizardAVR). Главное, чтобы скорость передачи UART микроконтроллера совпадала со ско- ростью приёма терминала. После настройки терминала следует записать программу в микроконтроллер (см. Запись программы в чип AVR) и запустить терминал (см. Команда Tools -> Terminal (Инструменты Терминал)). При этом в окне терминала можно наблю- дать информацию, которую передаёт в компьютер микроконтроллер AVR (Рис. 7.61). Рис. 7.61. Окно Terminal (Терминал) с полученной информацией при реализации проекта «Мах1241».
568 Глава 7. Примеры проектов Чтобы увидеть процесс передачи с самого начала, следует нажать кнопку «RESET» (см. Рис. 4.2). Информацию, принимаемую компьютером от микроконтроллерного уст- ройства, можно сохранить в файл. Для этого следует щёлкнуть по кнопке Rx File (Принять в файл) (Рис. 7.62) и в появившемся диалоговом окне Save received characters to a file (Сохранять полученные символы в файл) задать имя, выбрать расширение файла и щёлкнуть по кнопке Сохранить (Рис. 7.63). Рис. 7.62. Кнопка Rx File (Принять в файл) в окне Terminal (Терминал). Рис. 7.63. Диалоговое окно Save received characters to a file (Сохранять полученные символы в файл). Чтобы остановить сохранение получаемой информации в файл, следует щёл- кнуть по кнопке Rx Stop (Остановить приём) (Рис. 7.64). Д Terminal Disconnect | Hex Code: f Send(^| fix Stop | ^[x File | Hex | Clear [ Ф Rgset Chip| ИАХ1241 Demo using the CodeVisionAVR C Compiler SUMUl i. -W1* Puc. 7.64. Кнопка Rx Stop (Остановить приём) в окне Terminal (Терминал). Содержимое полученного файла можно просмотреть. Для этого следует щёлк- нуть по соответствующей кнопке на панели инструментов (Рис. 7.65), в появив- шемся диалоговом окне Open (Открыть) выбрать соответствующий файл и щёлк- нуть по кнопке Открыть (Рис. 7.66).
7.13. Проект «Maxi241» 569 Рис. 7.65. Кнопка Open file (Открыть файл) на панели инструментов. Рис. 7.66. Выбор файла в диалоговом окне Open (Открыть). После этого в области редактирования откроется окно с содержимым выбран- ного файла (Рис. 7.67). К D:\AVRTools\CodeVision_1__24_1c\Examples\Max1241\Max1241.txt fZ~[fn|[X| 1 2 3 4 5 6 7! 8 ИАХ1241 Demo using the CodeVisionAVR C Compiler ИАХ1241-> №2047 U»2498mV MAX1241-> №2047 U-2498mV MAX1241-> №2047 U=2498mV MAX1241-> №2047 U=2498mV HAX1241-> №2047 U=2498mV Puc. 7.67. Окно с содержимым файла Maxl241.txt.
570 Глава 7. Примеры проектов Следует отметить, что максимально возможный код результата преобразова- ния ОЬ 1111 1111 1111 = 4095 соответствует входному напряжению, равному VREF, т. е. при расчёте значения входного напряжения АЦП правильнее будет де- лить на 4095, а не на 4096. Для этого достаточно изменить строку 126 программы: 126: printf("МАХ1241-> N=%4u U=%4umV\r\n",n,(unsigned)((long)n*VREF/4095)); О передаче данных и работе с терминалом см. также Проект «Therm75. 7.14. Проект «AVR134» Этот пример демонстрирует часы реального времени, использующие второй внешний генератор микроконтроллера AVR. Значение реального времени отоб- ражается восемью светодиодами. В этом проекте содержится один исходный файл — avrl34.c. Текст этого фай- ла с русифицированными комментариями: 1: /**** П Р И К Л А Д Н О Е О П И С А Н И Е А V R 1 3 4 ************** 2: * 3: * Название: Часы реального времени 4: * Версия: 1.01 5: * Последнее обновление: 12.10.98 б: 7: * ***** АДАПТИРОВАНО ДЛЯ С Компилятора CodeVisionAVR ***** 8: * ***** HP InfoTech s.r.l. www.hpinfotech.ro ***** 9: ****** МОЖЕТ БЫТЬ ПРОТЕСТИРОВАНО КОМПЛЕКТОМ STK300 ***** 10: * 11: * Цель: АТтедаЮЗ (Все AVR - устройства со вторым внешним генератором) 12: 13: * Поддержка E-mail: avr@atmel.com 14: * 15: * Описание 16: * Это прикладное описание показывает, как осуществить часы реального 17: * времени, используя второй внешний генератор. Включена программа 18: * проверки выполнения этой функции, которая хранит текущее 19: * время, дату, месяц и год с автоматической конфигурацией високосного 20: * года. Чтобы отображать часы реального времени, используется 8 21: * светодиодов. 1-й светодиод мигает каждую секунду, следующие шесть 22: * представляют минуты, 8-й светодиод представляет час. 23. *********************************************************************/ 24: 25: // определения регистров ввода/вывода для АТтедаЮЗ 26: #include <megal03.h> 27: 28: // определения битов для регистра TIMSK 29: tfdefine TOIEO О 30: tfdefine OCIEO 1
7.14. Проект «AVR134» 571 31: И определения битов для регистра ASSR 32: tfdefine ASO 3 33: 34: char not_leap(void) ; 35: 36: typedef struct 37: ( 38: unsigned char second; //ввод текущего времени, даты, месяца и года 39: unsigned char minute; 40: unsigned char hour; 41: unsigned char date; 42: unsigned char month; 43: unsigned int year; 44: }t ime; 45: 46: time t; 47: 48: void main(void) 49: { 50: // инициализация часов реального времени 51: int tempO,templ; 52: 53: for(temp0=0;temp0<0x0040;temp0++) // Ожидание, чтобы 54: // стабилизировался внешний 55: // тактовый генератор 56: { 57: for(templ=0;templ<0xFFFF;templ++); 58: } 59: DDRB=0xFF; 60: TIMSK ((1<<TOIEO)1(1<<OCIEO)); // Запретим прерывание ТС 0, 61: ASSR 1 = (1<<ASO); // установим таймер/счётчик 0 62: // асинхронным с тактовой 63: // частотой CPU. Им управляет 64: // второй внешний тактовый 65: // генератор(32.768кГц) 66: TCNT0 = 0x00; 67: TCCR0 = 0x05; // к-т деления таймера - исходная частота / 128, 68: // чтобы каждое переполнение происходило точно через 69: //1 секунду 70: while(ASSR&0x07); // Ждём, пока ТС 0 не обновится 71: TIMSK |= (1«ТО1Е0); // устанавливаем разрешение прерывания по 72: // переполнению 8-битового таймера/счётчика 0 73: #asm("sei") // устанавливаем бит глобального разрешения прерывания 74: 75: while(1) 76: {
572 Глава 7. Примеры проектов 77: MCUCR = 0x38; // введём спящий режим: энергосберегающий режим 78: #asm("sleep") // пробуждение во время прерывания по переполнению 79: #asm("пор") 80: OCRO =0; // Запишем фиктивное значение в выходной регистр сравнения 81: while(ASSR&0x02); // ждём, пока OCR0 не обновится 82: } 83: } 84: 85: interrupt [TIM0_OVF] void counter(void) // вектор прерывания по 86: // переполнению 87: { 88: if ( ++t.second==60) // отслеживаем время, дату, месяц и год 89: { 90: t.second=0; 91: if (++t.minute==60) 92: { 93: t.minute=0; 94: if (++t.hour==24) 95: { 96: t.hour=0; 97: if (++t.date==32) 98: { 99: t.month*+; 100: t.date=l; 101: } 102: else if (t.date==31) 103: { 104: if ((t.month==4)11(t.month==6)11(t.month==9)11(t,month==ll)) 105: { 106: t.month*+; 107: t.date=l; 108: } 109: } 110: else if (t.date==30) 111: { 112: if(t.month==2) 113: { 114: t.month*+; 115: t.date=l; 116: } 117: } 118: else if (t.date==29) 119: { 120: if((t.month==2) && (not_leap())) 121: { 122: t.month*+;
7.14, Проект «AVR134» 573 123: t.date=l; 124: } 125: } 126: if (t.month==13) 127: { 128: t.month=l; 129: t.year++; 130: } 131: } 132: } 133: } 134: PORTB=~ ( ( (t. second&OxOl) 11 .minute«l) 11. hour<<7 ) ; 135: 136: } 137: 138: char not_leap(void) // изменения для високосного года 139: ( 140: if (!(t.year%100)) 141: return (char)(t.year%400); 142: else 143: return (char)(t.year%4); 144: } В этом файле знаками /* (начало) и */ (конец) выделены блоки с коммента- риями, а знаком // — строки с комментариями (см. Комментарии). В блоке с комментариями (строки 1 ...23) даётся общая информация о проекте. Директивой #include (строка 26) подключается заголовочный файл для ис- пользуемого микроконтроллера AVR (ATmegalO3) — файл megal03.h. Перед ком- пиляцией препроцессор компилятора вставит вместо этой строки текст соответ- ствующего файла (см. Директива ^include). Директивами #define (строки 29...32) определяются идентификаторы TOIEO, OCIEO и AS0. После этих строк перед компиляцией препроцессор компилятора заменит в тексте программы TOIEO на О, OCIEO на 1 и AS0 на 3. В случае модифи- кации программы для другого микроконтроллера AVR с другим положением би- тов TOIEO и OCIEO в регистре TIMSK и бита AS0 в регистре ASSR достаточно бу- дет лишь заменить значения в указанных директивах #define, не изменяя осталь- ного текста программы (см. Директива ttdefine, #undef). Директива #define очень удобна, но вовсе не обязательна. Строка 34 — это явное объявление (прототип) функции notjeap. Определи- тель char (беззнаковый байт) задаёт тип значения, которое возвращает функция (см. Типы данных). Далее идёт имя функции not_leap (может быть произвольным). Ключевое слово void в скобках указывает на то, что в эту функцию не передаются никакие параметры (см. Функции). Строки 36...44 — это определение типа данных, на что указывает ключевое слово typedef. Эта запись означает, что time — новое имя типа, представляющего
574 Глава 7. Примеры проектов собой структуру, состоящую из шести компонент: пяти переменных — second, minute, hour, date и month типа unsigned char (беззнаковый байт) и одной перемен- ной — year типа unsigned int (беззнаковое целое). В строке 46 объявляется переменная t типа time, который был определён в строках 36...44 программы. Строки 48...83 — это определение основной функции программы (main). Эта функция обязательно должна присутствовать во всех программах (но только ОДНА!). Именно с этой функции, в каком бы месте программы она ни находи- лась, начинается выполнение программы. В строке 48 осуществляется объявление функции main. Вначале стоит ключе- вое слово void, указывающее на то, что эта функция не возвращает никаких зна- чений, затем следует имя функции main (ЕГО ИЗМЕНЯТЬ НЕЛЬЗЯ!) и ключевое слово void в скобках, которое указывает на то, что в эту функцию не передаются никакие параметры (см. Функции), В строке 49 открывающая фигурная скобка указывает на начало тела функции main. В строке 51 объявляются локальные переменные tempO и tempi типа int (це- лое) (см. Типы данных). В строках 53...58 реализована временная задержка для стабилизации внешне- го тактового генератора. В строке 53 находится оператор цикла for (см. Оператор for). Тело этого опера- тора, заключённое в фигурные скобки, находящиеся в строках 56 (открывающая скобка) и 58 (закрывающая скобка), будет выполняться 64 раза (от tempO = 0 до tempO = OxOO3F = 63, т. е. пока tempO < 0x0040). В строке 57 находится ещё один оператор for, который не имеет тела. Так как переменная tempi типа int (целое), то число OxFFFF, стоящее в условии оператора for, компилятором будет интерпретироваться как число со знаком (см. Типы дан- ных), т. е. OxFFFF будет означать —1, и именно с этим числом будет сравниваться значение переменной tempi. Таким образом, условие выполнения данного опера- тора for сразу будет ложным, т. к. 0 > —1, и выполнение программы после провер- ки данного условия сразу перейдёт на строку 58. Моделирование в AVR Studio (см., например, Работа с отладчиком AVR Studio) показывает, что временная задержка, реализованная в строках 53...58 программы, для микроконтроллера ATmegalO3 при тактовой частоте 4 МГц составляет 241.75 мкс. Разумеется, данную задержку также можно реализовать с помощью функций задержки (см., например, Проект «ADC8535»). В строке 59 в регистр DDRx Порта В во все биты записываются 1 (Oxff=Ob 1111 1111), тем самым все выводы этого порта делаются выходами (см. Доступ к регистрам ввода/вывода). В строке 60 перед компиляцией препроцессор заменит идентификатор TOIE0 на 0 (см. строку 29), a OCIE0 — на 1 (см. строку 30). В этой строке согласно приори- тетам операций (см. Приоритеты операций и порядок вычислений) сначала будут вы- полнены операции в скобках (приоритет 1), т. е. сначала первое значение 1 будет сдвинуто (знак операции «, см. Бинарные операции) на 0 битов влево (т. е. останется равным 1 = 0Ь 0000 0001), а второе значение 1 — на 1 бит влево
7.14. Проект «AVR134» 575 (т. е. вместо 1 = ОЬ 0000 0001 станет ОЬ 0000 0010). Затем между этими двумя полу- ченными значениями осуществляется операция побитного ИЛИ (знак операции |). При этом каждый бит первого операнда сравнивается с соответствующим битом второго операнда. Если любой (или оба) из сравниваемых битов равен 1, то соот- ветствующий бит результата устанавливается в 1, в противном случае — в 0 (см. Бинарные операции). В результате (0Ь 0000 0001) | (0Ь 00000010) = 0Ь 0000 ООП. Далее с полученным значением выполняется операция побитового логического от- рицания (знак операции ~, приоритет 2), т. е. все 1 в полученном значении заменят- ся на 0, а 0 — на 1 (см. Унарные операции). В результате ~0Ь 0000 ООП = 0Ь 11111100. Последней выполняется операция побитного И с присваиванием (знак опе- рации &=, приоритет 14) полученного значения со значением регистра маски прерывания от таймеров/счётчиков TIMSK. При этом каждый бит первого опе- ранда сравнивается с соответствующим битом второго операнда. Если оба срав- ниваемых бита 1, то соответствующий бит результата устанавливается в 1, в про- тивном случае — в 0 (см. Бинарные операции). Так как первоначальное (после включения питания или после сброса) значение регистра TIMSK — 0Ь 0000 0000, то в результате получится: 0Ь 0000 0000 & 0Ь 11111100 = 0Ь 0000 0000. Полученное значение записывается в регистр TIMSK. Таким образом, прерывание от тайме- ра/счётчика 0 будет запрещено. Разумеется, в данном случае можно сразу записать значение 0 в регистр TIMSK: 60: TIMSK = 0x00; // Запрещаем прерывание TC0, Но запись, приведённая в примере, является более универсальной. Её смысл — запретить прерывания от таймера/счётчика 0. Для этого необходимо установить в 0 биты TOIE0 (бит разрешения прерывания по переполнению таймера/счётчика 0) и OCIE0 (бит разрешения прерывания по совпадению таймера/счётчика 0) регистра TIMSK. Но в различных микроконтроллерах эти биты находятся на разных местах в регистре TIMSK, и, кроме того, в этом регистре может быть записано какое-то другое (ненулевое) значение. То есть этот оператор может быть расположен в дру- гом месте программы. При переходе на другой микроконтроллер AVR достаточно будет лишь изменить директивы #define в строках 29 и 30, указав положение битов в регистре TIMSK. Тогда запись, приведённая в примере, обнулит только биты TOIE0 и OCIE0, не изменяя остальные биты в регистре TIMSK. В строке 61 перед компиляцией препроцессор заменит идентификатор AS0 на 3 (см. строку 32). В этой строке, согласно приоритетам операций (см. Приоритеты операций и порядок вычислений), сначала будет выполнена операция в скобках (при- оритет 1), т. е. сначала значение 1 будет сдвинуто влево (знак операции «, см. Би- нарные операции) на 3 бита, т. е. вместо 1=0Ь 0000 0001 станет 0Ь 0000 1000. Затем выполняется операция побитного ИЛИ с присваиванием (знак операции |=, при- оритет 14) полученного значения со значением регистра статуса асинхронного ре- жима ASSR. При этом каждый бит первого операнда сравнивается с соответствую- щим битом второго операнда. Если любой (или оба) из сравниваемых битов равен 1, то соответствующий бит результата устанавливается в 1, в противном случае — в 0 (см. Бинарные операции). Так как первоначальное (после включения питания или после сброса) значение регистра ASSR — 0Ь 0000 0000, то в результате получится:
576 Глава 7. Примеры проектов ОЬ 0000 000010Ь 0000 0100 = 0Ь 0000 0100. Полученное значение записывается в ре- гистр ASSR. Таким образом, таймер/счётчик 0 будет асинхронно тактироваться сигналом с вывода TOSC1, что позволяет использовать таймер/счётчик 0 в качестве часов реального времени. Генератор оптимизирован под использование кварцево- го резонатора с частотой 32768 кГц. Разумеется, в данном случае можно сразу записать значение 1 в бит AS0 ре- гистра ASSR: 61: 62: 63: 64: 65: ASSR = ОЬООООЮОО; // установим таймер/счётчик О // асинхронным с тактовой // частотой CPU. Им управляет // второй внешний тактовый // генератор(32.768кГц) Но запись, приведённая в примере, является более универсальной. Её смысл — установить асинхронный режим работы таймера/счётчика 0. Для этого необходимо установить в 1 бит AS0 (бит асинхронного режима таймера/счётчика 0) регистра ASSR. Но в различных микроконтроллерах этот бит находится на разных местах в регистре ASSR, и, кроме того, в этом регистре может быть записано какое-то другое (ненулевое) значение. То есть этот оператор может быть расположен в другом месте программы. При переходе на другой микроконтроллер AVR, достаточно будет лишь изменить директиву #define в строке 32, указав положение бита AS0 в регис- тре ASSR. Тогда запись, приведённая в примере, установит в 1 только бит AS0, не изменяя остальные биты в регистре ASSR. В строке 66 в регистр TCNT0, в котором хранится текущее значение тайме- ра/счётчика 0, записывается значение 0 = 0x00, т. е. таймер/счётчик 0 обнуляется. В строке 67 в регистр управления таймера/счётчика 0 TCCR0 записывается значение 0x05 = 0Ь 0000 0101, т. е. коэффициент деления предварительного дели- теля частоты таймера/счётчика 0 устанавливается равным 128. В строке 70 реализован цикл с помощью оператора while (см. Оператор while). Этот оператор while будет выполняться до тех пор, пока выражение в скобках ис- тинно, т. е. значение выражения (ASSR & 0x07) не ноль. В этом выражении вы- полняется операция побитного И (знак операции &) значения регистра ASSR с числом 0x07 = 0Ь 0000 0111. При этом каждый бит первого операнда сравнивается с соответствующим битом второго операнда. Если оба сравниваемых бита 1, то соответствующий бит результата устанавливается в 1, в противном случае — в 0 (см. Бинарные операции). Значение этого выражения станет равным нулю, когда значение всех трёх самых младших битов (0...2) регистра ASSR станет равным 0. Значение этих битов аппаратно очищается (сбрасывается в 0) при обновлении со- держимого регистра TCNT0. Таким образом, оператор while будет выполняться до тех пор, пока не обновится содержимое таймера/счётчика 0. В строке 71 перед компиляцией препроцессор заменит идентификатор TOIE0 на 0 (см. строку 29). В этой строке согласно приоритетам операций (см. Приори- теты операций и порядок вычислений) сначала будет выполнена операция в скоб- ках (приоритет 1), т. е. сначала значение 1 будет сдвинуто влево (знак операции «, см. Бинарные операции) на 0 бит, т. е. останется равным 1 = 0Ь 0000 0001. За- тем выполняется операция побитового ИЛИ с присваиванием (знак операции |=,
7.14. Проект «AVR134» 577 приоритет 14) полученного значения со значением регистра TIMSK. При этом каждый бит первого операнда сравнивается с соответствующим битом второго операнда. Если любой (или оба) из сравниваемых битов равен 1, то соответству- ющий бит результата устанавливается в 1, в противном случае — в 0 (см. Бинар- ные операции). Так как значение регистра TIMSK после выполнения строки 60 программы — 0Ь 0000 0000, то в результате получится: 0Ь 0000 0000 | 0Ь 0000 0001 = 0Ь 0000 0001. Полученное значение записывается в регистр TIMSK. Таким об- разом, разрешается прерывание по переполнению таймера/счётчика 0. Разумеется, в данном случае можно сразу записать значение 1 в бит TOIEO ре- гистра TIMSK: 71: TIMSK = ObOOOOOOOl; // устанавливаем разрешение прерывания по 72: // переполнению 8-битового таймера/счётчика 0 Но запись, приведённая в примере, является более универсальной. Её смысл — разрешить прерывание по переполнению таймера/счётчика 0. Для этого необходимо установить в 1 бит TOIEO (бит разрешения прерывания по перепол- нению таймера/счётчика 0) регистра TIMSK. Но в различных микроконтролле- рах этот бит находится на разных местах в регистре TIMSK, и, кроме того, в этом регистре может быть записано какое-то другое (ненулевое) значение. То есть этот оператор может быть расположен в другом месте программы. При переходе на другой микроконтроллер AVR достаточно будет лишь изменить директиву #define в строке 29, указав положение бита TOIEO в регистре TIMSK. Тогда запись, при- ведённая в примере, установит в 1 только бит TOIEO, не изменяя остальные биты в регистре TIMSK. Подробнее о соответствующих регистрах микроконтроллера и их битах см. описание (datasheet) от Atmel на используемый микроконтроллер (см. Команда Help -> AVR Data Sheets (Помощь -> Описания AVR)). В строке 73 осуществляется глобальное разрешение прерываний. Для этого в программу включён ассемблерный код (см. Директивы #asm и ttendasm). Инс- трукция ассемблера sei устанавливает фчаг глобального прерывания I в регистре статуса SREG микроконтроллера, тем самым разрешая глобальные прерывания. Подробнее об инструкциях ассемблера см. систему помощи бесплатной програм- мы AVR Studio от Atmel, которую можно скачать на сайте разработчиков микро- контроллеров AVR http://www.atmel.com. В строке 75 реализован бесконечный цикл с помощью оператора while (см. Оператор while). Этот оператор будет выполняться до тех пор, пока выраже- ние в скобках истинно, т. е. не 0. Выражение в скобках — 1 (можно также напи- сать 2 или 73), а не 0, т. е. всегда истинно. Следовательно, этот оператор будет вы- полняться бесконечно, пока не произойдёт прерывание программы. В этом цикле бесконечно будет выполняться группа операторов, заключённая в фигурные скобки, находящиеся в строках 76 (открывающая скобка) и 82 (за- крывающая скобка). В строке 77 в регистр управления микроконтроллером MCUCR записывается значение 0x38 = 0Ь ООП 1000, тем самым разрешается перевод микроконтролле- ра в один из режимов SLEEP (режим Power Save — экономичный режим
578 Глава 7. Примеры проектов энергосбережения) по команде «sleep». Если таймер/счётчик 0 тактируется асинх- ронно, т. е. бит AS0 в регистре ASSR установлен, то в режиме Power Save тай- мер/счётчик 0 будет работать. Микроконтроллер будет активироваться прерыва- ниями по переполнению или по совпадению выхода таймера/счётчика 0. В строке 78 микроконтроллер переводится в режим SLEEP. Для этого в про- грамму включён ассемблерный код (см. Директивы #asm и ttendasm). Инструкция ассемблера sleep переводит схему в один из режимов SLEEP, который определяет- ся регистром управления микроконтроллером MCUCR, т. е. в данном случае в ре- жим Power Save. Когда прерывание выводит микроконтроллер из режима SLEEP, то сначала будет выполнена команда, следующая за командой sleep, а затем програм- ма перейдёт в программу обработки прерывания. По этой причине в программу (строка 79) включена ассемблерная инструкция пор, которая ничего не делает. Подробнее об инструкциях ассемблера см. систему помощи бесплатной про- граммы AVR Studio от Atmel, которую можно скачать на сайте разработчиков мик- роконтроллеров AVR http://www.atmel.com. В строке 80 в регистр сравнения выхода таймера/счётчика О OCRO записыва- ется фиктивное значение 0. Совпадение значений регистров OCRO и TCNT0 при- ведёт к установке флага прерывания по совпадению, но прерывания не произой- дёт, т. к. прерывание по совпадению запрещено (см. строку 60). Совпадение при сравнении произойдёт только тогда, когда таймер/счётчик 0 досчитает до значе- ния содержимого регистра сравнения OCRO. Программная запись одного и того же значения в таймер/счётчик 0 и в регистр сравнения выхода не приведёт к фор- мированию совпадения при сравнении. В строке 81 реализован цикл с помощью оператора while (см. Оператор while). Этот оператор while будет выполняться до тех пор, пока выражение в скобках ис- тинно, т. е. значение выражения (ASSR & 0x02) не ноль. В этом выражении вы- полняется операция побитного И (знак операции &) значения регистра ASSR с числом 0x02 = 0Ь 0000 0010. При этом каждый бит первого операнда сравнивает- ся с соответствующим битом второго операнда. Если оба сравниваемых бита 1, то соответствующий бит результата устанавливается в 1, в противном случае — в 0 (см. Бинарные операции). Значение этого выражения станет равным нулю, когда значение 1-го бита регистра ASSR станет равным 0. Этот бит устанавливается в состояние 1 при работе таймера/счётчика 0 в асинхронном режиме и записанном регистре OCR0. При обновлении записанного в регистр OCR0 значения бит ап- паратно очищается (сбрасывается в 0). Значение 0 этого бита означает, что ре- гистр OCR0 готов к обновлению новым значением. Так как в предыдущей строке программы делается фиктивное обновление значения регистра OCR0, то значе- ние 1-го бита регистра ASSR будет равно 0. Таким образом, этот оператор while будет пропущен, т. к. выражение в скобках равно 0, и выполнение программы пе- рейдёт на строку 82, а затем на строку 77. В строке 83 закрывающая фигурная скобка указывает на конец тела функции main. Строки 85...136 — это определение программы (функции) обслуживания пре- рывания по переполнению таймера/счётчика 0. В строке 85 осуществляется доступ к системе прерываний микроконтроллера AVR, на что указывает ключевое слово interrupt. Далее за этим ключевым словом
7.14. Проект «AVR134» 579 в квадратных скобках должен идти номер вектора прерывания. Но здесь вместо номера стоит идентификатор TIMO_OVE Дело в том, что этот идентификатор оп- ределён директивой #define в заголовочном файле megal03.h. Перед компиляцией препроцессор компилятора заменит TIMO_OVF на цифру 17 (номер вектора пре- рывания), как определено в файле megal03.h. Вместо TIMO_OVF в квадратных скобках можно было написать 17, но TIMO_OVF нагляднее. Далее идёт ключевое слово void, указывающее на то, что функция не возвращает никаких значений, затем имя функции counter (может быть произвольным, но лучше со смысловой нагрузкой). По этому имени далее в программе осуществляется вызов данной функции. Ключевое слово void в скобках указывает на то, что в эту функцию не передаются никакие параметры. Подробнее см. Использование прерываний. В строке 87 открывающая фигурная скобка указывает на начало тела програм- мы обслуживания прерывания. В строках 88... 133 находится укороченный оператор if-else (без else) (см. Опе- ратор if-else). В строке 88 — начало этого оператора, на что указывает ключевое слово if. Выражение в скобках (условие оператора if-else) будет истинно, если значение выражения ++t.second равно 60 (знак операции ==, см. Бинарные операции). В противном случае выражение ложно. При вычислении выражения в скобках те- кущее значение компонента second структуры t (обозначение t.second) СНАЧА- ЛА увеличивается на 1 (операция инкремента, знак операции — ++, см. Унар- ные операции), а затем сравнивается с числом 60, т. е. это выражение будет ис- тинно, если текущее значение компонента t.second было равно 59. Если выражение в скобках истинно, то будет выполняться группа операторов (составной оператор), заключённая в фигурные скобки, находящиеся в строках 89 (открывающая скобка) и 133 (закрывающая скобка). После этого выполнение программы будет продолжено со строки 134. Если выражение в скобках ложно, то выполнение программы будет сразу продолжено со строки 134. В строке 90 компоненту second структуры t (обозначение t.second) присваива- ется значение 0. В строках 91... 132 находится укороченный оператор if-else (без else) (см. Опе- ратор if-else). В строке 91 — начало этого оператора, на что указывает ключевое слово if. Вы- ражение в скобках (условие оператора if-else) будет истинно, если значение выра- жения ++t.minute равно 60 (знак операции ==, см. Бинарные операции). В против- ном случае выражение ложно. При вычислении выражения в скобках текущее значение компонента minute структуры t (обозначение t.minute) сначала увеличи- вается на 1 (операция инкремента, знак операции ++, см. Унарные операции), а затем сравнивается с числом 60, т. е. это выражение будет истинно, если текущее значение компонента t.minute было равно 59. Если выражение в скобках истинно, то будет выполняться группа операторов (составной оператор), заключённая в фигурные скобки, находящиеся в строках 92 (открывающая скобка) и 132 (закрывающая скобка). После этого выполнение программы будет продолжено со строки 134. Если выражение в скобках ложно, то выполнение программы будет сразу продолжено со строки 134.
580 Глава 7. Примеры проектов В строке 93 компоненту minute структуры t (обозначение t.minute) присваива- ется значение 0. В строках 94... 131 находится укороченный оператор if-else (без else) (см. Опе- ратор if-else). В строке 94 — начало этого оператора, на что указывает ключевое слово if. Вы- ражение в скобках (условие оператора if-else) будет истинно, если значение выра- жения ++t.hour равно 24 (знак операции ==, см. Бинарные операции). В против- ном случае выражение ложно. При вычислении выражения в скобках текущее значение компонента hour структуры t (обозначение t.hour) сначала увеличивает- ся на 1 (операция инкремента, знак операции — ++, см. Унарные операции), а за- тем сравнивается с числом 24, т. е. это выражение будет истинно, если текущее значение компонента t.hour было равно 23. Если выражение в скобках истинно, то будет выполняться группа операторов (составной оператор), заключённая в фигурные скобки, находящиеся в строках 95 (открывающая скобка) и 131 (закрывающая скобка). После этого выполнение программы будет продолжено со строки 134. Если выражение в скобках ложно, то выполнение программы будет сразу продолжено со строки 134. В строке 96 компоненту hour структуры t (обозначение t.hour) присваивается значение 0. В строках 97... 125 находится оператор if-else (см. Оператор if-else). В строке 97 — начало этого оператора, на что указывает ключевое слово if. Вы- ражение в скобках (условие оператора if-else) будет истинно, если значение выра- жения ++t.date равно 32 (знак операции ==, см. Бинарные операции). В против- ном случае выражение ложно. При вычислении выражения в скобках текущее значение компонента date структуры t (обозначение t.date) сначала увеличивается на 1 (операция инкремента, знак операции ++, см. Бинарные операции), а затем сравнивается с числом 32, т. е. это выражение будет истинно, если текущее значе- ние компонента t.hour было равно 31. Если выражение в скобках истинно, то будет выполняться группа операторов (составной оператор), заключённая в фигурные скобки, находящиеся в строках 98 (открывающая скобка) и 101 (закрывающая скобка). После этого выполнение программы будет продолжено со строки 126. Если выражение в скобках ложно, то выполнение программы перейдёт на строку 102, где находится соответствующее ключевое слово else. В строке 99 текущее значение компонента month структуры t (обозначение t.month) увеличивается на 1 (операция инкремента, знак операции ++, см. Унар- ные операции.). В строке 100 компоненту date структуры t (обозначение t.date) присваивается значение 1 (знак операции =, см. Бинарные операции). В строке 102 — находится ключевое слово else оператора if-else, означающее, что следующий за ним оператор будет выполняться, если условие оператора if-else (строка 97) ложно. Следующий за else оператор расположен в этой же строке — это оператор if-else. В строке 102 начало этого оператора, на что указывает ключевое слово if. Выражение в скобках (условие оператора if-else) будет истинно, если текущее
7.14. Проект «AVR134» 581 значение компонента date структуры t (обозначение t.date) равно 31 (знак опера- ции ==, см. Бинарные операции), т. е. если до операции инкремента в строке 97 оно было равно 30. В противном случае выражение ложно. Если выражение в скобках истинно, то будет выполняться группа операторов (составной оператор), заключённая в фигурные скобки, находящиеся в строках 103 (открывающая скобка) и 109 (закрывающая скобка). После этого выполнение программы будет продолжено со строки 126. Если выражение в скобках ложно, то выполнение программы перейдёт на строку ПО, где находится соответствующее ключевое слово else. В строках 104... 108 находится укороченный оператор if-else (без else) (см. Оператор if-else). В строке 104 — начало этого оператора, на что указывает ключевое слово if. При вычислении выражения в скобках (условие оператора if-else): (t.month == 4) || (t.month == 6) || (t.month == 9) || (t.month == 11) текущее значение компонента month структуры t (обозначение t.month) срав- нивается с числами 4, 6, 9 и 11. При равенстве с одним из этих значений (знак операции ==, см. Бинарные операции) соответствующее значение выражения ста- новится равным 1 (истина), в противном случае — 0 (ложь). Затем выполняются операции логического ИЛИ (знак операции — ||), которые вырабатывают значе- ние 0, если оба операнда имеют значение 0. Если какой-либо из операндов имеет ненулевое значение, то результат операции равен 1. Таким образом, условие этого оператора if-else будет истинным, если текущее значение компонента t.month рав- но одному из чисел 4, 6, 9 или 11. В противном случае условие ложно. Если условие оператора if-else истинно, то будет выполняться группа операто- ров (составной оператор), заключённая в фигурные скобки, находящиеся в стро- ках 105 (открывающая скобка) и 108 (закрывающая скобка). После этого выпол- нение программы будет продолжено со строки 126. Если условие оператора if-else ложно, то выполнение программы будет сразу продолжено со строки 126. В строке 106 текущее значение компонента month структуры t (обозначение t.month) увеличивается на 1 (знак операции ++, см. Унарные операции). В строке 107 компоненту date структуры t (обозначение t.date) присваивается значение 1 (знак операции =, см. Бинарные операции). В строке 110 находится ключевое слово else оператора if-else, означающее, что следующий за ним оператор будет выполняться, если условие оператора if-else (строка 102) ложно. Следующий за else оператор расположен в этой же строке — это оператор if-else. В строке 110 — начало этого оператора, на что указывает ключевое слово if. Выражение в скобках (условие оператора if-else) будет истинно, если текущее значение компонента date структуры t (обозначение t.date) равно 30 (знак опера- ции ==, см. Бинарные операции), т. е. если до операции инкремента в строке 97 оно было равно 29. В противном случае выражение ложно. Если выражение в скобках истинно, то будет выполняться группа операторов (составной оператор), заключённая в фигурные скобки, находящиеся в строках 111 (открывающая скобка) и 117 (закрывающая скобка). После этого выполнение программы будет продолжено со строки 126. Если выражение в скобках ложно, то
582 Глава 7. Примеры проектов выполнение программы перейдёт на строку 118, где находится соответствующее ключевое слово else. В строках 112...116 находится укороченный оператор if-else (без else) (см. Оператор if-else). В строке 112 — начало этого оператора, на что указывает ключевое слово if. Выражение в скобках (условие оператора if-else) будет истинно, если текущее значение компонента month структуры t (обозначение t.month) равно 2 (знак опе- рации ==, см. Бинарные операции). В противном случае выражение ложно. Если выражение в скобках истинно, то будет выполняться группа операторов (составной оператор), заключённая в фигурные скобки, находящиеся в строках 113 (открывающая скобка) и 116 (закрывающая скобка). После этого выполнение программы будет продолжено со строки 126. Если выражение в скобках ложно, то выполнение программы будет сразу продолжено со строки 126. В строке 114 текущее значение компонента month структуры t (обозначение t.month) увеличивается на 1 (знак операции ++, см. Унарные операции). В строке 115 компоненту date структуры t (обозначение t.date) присваивается значение 1 (знак операции =, см. Бинарные операции). В строке 118 находится ключевое слово else оператора if-else, означающее, что следующий за ним оператор будет выполняться, если условие оператора if-else (строка 110) ложно. Следующий за else оператор расположен в этой же строке — это укороченный оператор if-else (без else) (см. Оператор if-else). В строке 118 — начало этого опера- тора, на что указывает ключевое слово if. Выражение в скобках (условие операто- ра if-else) будет истинно, если текущее значение компонента date структуры t (обозначение t.date) равно 29 (знак операции ==, см. Бинарные операции), т. е. ес- ли до операции инкремента в строке 97 оно было равно 28. В противном случае выражение ложно. Если выражение в скобках истинно, то будет выполняться группа операторов (составной оператор), заключённая в фигурные скобки, находящиеся в строках 119 (открывающая скобка) и 125 (закрывающая скобка). После этого выполнение программы будет продолжено со строки 126. Если выражение в скобках ложно, то выполнение программы будет сразу продолжено со строки 126. В строках 120... 124 находится укороченный оператор if-else (без else) (см. Оператор if-else). В строке 120 — начало этого оператора, на что указывает ключевое слово if. При вычислении выражения в скобках (условие оператора if-else): (t.month == 2) && (not_leap()) сначала выполняются выражения в скобках. Текущее значение компонента month структуры t (обозначение t.month) сравнивается с числом 2. При их равенс- тве (знак операции ==, см. Бинарные операции) соответствующее значение выра- жения в скобках становится равным 1 (истина), в противном случае — 0 (ложь). Затем вызывается функция not_leap и выполняется операция логического И (знак операции &&, см. Бинарные операции) между значением, которое возвращает фун- кция not_leap, и результатом сравнения t.month с числом 2. Операция логического И (&&) вырабатывает значение 1, если оба операнда имеют ненулевые значения.
7.14, Проект «AVR134» 583 Если один из операндов равен 0, то результат также равен 0. Таким образом, усло- вие этого оператора if-else будет истинным, если и значение компонента t.month равно 2 и функция not_leap вернёт ненулевое значение. В противном случае усло- вие ложно. Если условие оператора if-else истинно, то будет выполняться группа операто- ров (составной оператор), заключённая в фигурные скобки, находящиеся в стро- ках 121 (открывающая скобка) и 124 (закрывающая скобка). После этого выпол- нение программы будет продолжено со строки 126. Если условие оператора if-else ложно, то выполнение программы будет сразу продолжено со строки 126. В строке 122 текущее значение компонента month структуры t (обозначение t.month) увеличивается на 1 (знак операции ++, см. Унарные операции). В строке 123 компоненту date структуры t (обозначение t.date) присваивается значение 1 (знак операции =, см. Бинарные операции). В строках 126... 130 находится укороченный оператор if-else (без else) (см. Оператор if-else). В строке 126 — начало этого оператора, на что указывает ключевое слово if. Выражение в скобках (условие оператора if-else) будет истинно, если текущее зна- чение компонента month структуры t (обозначение t.month) равно 13 (знак опера- ции ==, см. Бинарные операции). В противном случае выражение ложно. Если выражение в скобках истинно, то будет выполняться группа операторов (составной оператор), заключённая в фигурные скобки, находящиеся в строках 127 (открывающая скобка) и 130 (закрывающая скобка). После этого выполнение программы будет продолжено со строки 134. Если выражение в скобках ложно, то выполнение программы будет сразу продолжено со строки 134. В строке 128 компоненту month структуры t (обозначение t.month) присваива- ется значение 1 (знак операции =, см. Бинарные операции). В строке 129 значение компонента year структуры t (обозначение t.year) уве- личивается на 1 (операция инкремента, знак операции ++, см. Унарные опера- ции). В строке 134 в соответствии с приоритетами операций (см. Приоритеты опе- раций и порядок вычислений) сначала выполняются операции в скобках (приоритет 1), т. е. первой выполняется операция побитного И (знак операции &) текущего значения компонента t.second (0b ХХХХ ХХХХ) с числом 0x01 = 0Ь 0000 0001. При этом каждый бит первого операнда сравнивается с соответствующим битом вто- рого операнда. Если оба сравниваемых бита 1, то соответствующий бит результата устанавливается в 1, в противном случае — в 0 (см. Бинарные операции). Результат этой операции: 0Ь ХХХХ ХХХХ & 0Ь 0000 0001 = 0Ь 0000 000Х. Второй выполняется операция сдвига влево (знак операции «, приоритет 5). При этом текущее значение компонента t.minute сдвигается на 1 бит влево, т. е. значение 0-го бита перейдёт в 1-й бит, 1-го — во 2-й, ..., 6-го — в 7-й. Значение 7-го бита пропадёт, а в 0-й бит будет записан 0. Третьей выполняется операция побитного ИЛИ (знак операции |, приоритет 10) между результатами первой и второй операции. При этом каждый бит первого операнда сравнивается с соответствующим битом второго операнда. Если любой (или оба) из сравниваемых битов равен 1, то соответствующий бит результата ус- танавливается в 1, в противном случае — в 0 (см. Бинарные операции).
584 Глава 7. Примеры проектов Четвёртой выполняется операции сдвига влево (знак операции «, приори- тет 5). При этом текущее значение компонента t.hour сдвигается на 7 битов вле- во, т. е. значение 0-го бита перейдёт в 7-й. Значение битов с 1-го по 6-й пропадёт, и в них будет записан 0. Пятой выполняется операция побитного ИЛИ (знак операции |, приоритет 10) между результатами третьей и четвёртой операции. При этом каждый бит пер- вого операнда сравнивается с соответствующим битом второго операнда. Если любой (или оба) из сравниваемых битов равен 1, то соответствующий бит резуль- тата устанавливается в 1, в противном случае — в 0 (см. Бинарные операции). Затем с полученным результатом выполняется операция побитового логичес- кого отрицания (знак операции ~). При этом все 1 в значении результата преды- дущих операций заменяются на 0, а 0 — на 1 (см. Унарные операции). Полученное значение записывается в регистр PORTx Порта В (см. Доступ к регистрам вво- да/вывода). Таким образом, первый светодиод, поключённый к самому младшему биту (биту 0) Порта В, будет отображать самый младший бит значения компонента t.second. Значение этого бита изменяется каждую секунду. Следующие 6 светоди- одов, подключённые к битам 1...6 Порта В, будут отображать 6 младших битов компонента t.minute, т. е. значение минут. Последний, восьмой светодиод, под- ключённый к самому старшему биту (биту 7) Порта В, будет отображать самый младший бит значения компонента t.hour. Значение этого бита изменяется каж- дый час. В строке 136 закрывающая фигурная скобка указывает на конец тела програм- мы (функции) обслуживания прерывания. Алгоритм работы функции counter представлен блок-схемой на Рис. 7.68. Строки 138... 144 — это определение функции not_leap. В строке 138 осуществляется объявление функции. Определитель char (байт) за- даёт тип значения, которое возвращает функция (см. Типы данных). Далее идёт имя функции notjeap (может быть произвольным). По этому имени далее в программе осуществляется вызов данной функции. Ключевое слово void в скобках указывает на то, что в эту функцию не передаются никакие параметры (см. Функции). В строке 139 открывающая фигурная скобка указывает на начало тела функ- ции notjeap. В строках 140... 143 находится оператор if-else (см. Оператор if-else). В строке 140 — начало этого оператора, на что указывает ключевое слово if. Выражение в скобках (условие оператора if-else) будет истинно, если значение выражения !(t.year% 100) не равно 0. В этом выражении сначала вычисляется ос- таток от деления текущего значения компонента year структуры t (обозначение t.year) на 100 (знак операции %, см. Бинарные операции), а затем с этим остатком выполняется операция логического отрицания (знак операции !; см. Бинарные операции). При этом если остаток равен 0, то результатом операции логического отрицания будет 1. При всех других значениях остатка результатом операции ло- гического отрицания будет 0. Таким образом, условие этого оператора if-else будет истинно, если текущее значение t.year нацело (без остатка) делится на 100. В про- тивном случае условие ложно.
7.14, Проект «AVR134» 585 year = year + 1 month = 1 PORTB=~( ( {t.second&OxOl) : 11 .minute«l) 11. hour«7) * Return Puc. 7.68. Блок-схема функции counter.
586 Глава 7. Примеры проектов Если выражение в скобках истинно, то будет выполняться оператор, находя- щийся в строке 141. Если выражение в скобках ложно, то выполнение программы перейдёт на строку 142, где находится соответствующее ключевое слово else. В строке 141 находится оператор return, который завершает выполнение фун- кции, в которой он задан, и возвращает управление в вызывающую функцию (см. Оператор return). Значение выражения, стоящего за оператором return, воз- вращается в вызывающую функцию в качестве значения вызываемой функции. В этом выражении сначала вычисляется остаток от деления текущего значения компонента t.year на 400 (знак операции %, см. Бинарные операции). Затем это значение приводится к типу char (байт) (см. Преобразования типов), и результат возвращается в вызывающую функцию. В строке 142 находится ключевое слово else оператора if-else, означающее, что следующий за ним оператор будет выполняться, если условие оператора if-else (строка 140) ложно. Следующий за else оператор, расположенный в строке 143, — это оператор return (см. строку 141). В выражении, которое стоит за этим оператором, сначала вычисляется остаток от деления текущего значения компонента t.year на 4 (знак операции %, см. Бинарные операции). Затем это значение приводится к типу char (байт) (см. Преобразования типов), и результат возвращается в вызывающую фун- кцию. Таким образом, если при вызове функции notjeap текущее значение t.year на- цело (без остатка) делится на 100, то функция возвращает остаток от деления — значение t.year на 400. В противном случае функция not_leap возвращает остаток отделения — значение t.year на 4. По определению, високосным годом считается год, число которого делится на 4, кроме годов, числа которых оканчиваются на 2 нуля, но не делятся на 400 (например, 1700, 1800), т. е. функция notjeap возвращает 0, если текущий год ви- сокосный. В противном случае функция notjeap возвращает ненулевое значение. Алгоритм работы функции notjeap представлен блок-схемой на Рис. 7.69. return year % 4 Рис. 7.69. Блок-схема функции notjeap. Так как в настройках проекта на закладке С Compiler (Компилятор Си) выбра- на опция char is unsigned (байт является беззнаковым) (Рис. 7.70), то все данные типа char (байт) компилятор будет обрабатывать как данные типа unsigned char
7.14. Проект «AVR134» 587 (беззнаковый байт) (см. Закладка С Compiler (Компилятор Си)). То есть значение, которое возвращает функция notjeap, будет иметь тип unsigned char (беззнако- вый байт). Настоятельно рекомендуется явно определять тип данных в тексте про- граммы, т. к. при распечатке текста программы настроек проекта не видно. Это может вызвать неправильное понимание работы программы! Программа работает следующим образом. После подачи питания (или аппа- ратного сброса на выводе RESET микроконтроллера) начинает выполняться функция main. При этом объявляются переменные tempO и tempi и с помощью операторов for выполняется временная задержка для стабилизации внешнего так- тового генератора. Затем выполняется инициализация периферийных устройств (портов, регистров и таймера/счётчика 0) таким образом, чтобы прерывание про- граммы происходило через каждую секунду, и устанавливается разрешение пре- рывания по переполнению таймера/счётчика 0. Рис. 7.70. Опция char is unsigned (байтявляется беззнаковым) выбрана. После этого программа переходит в бесконечный цикл, в котором микрокон- троллер переводится в спящий режим и находится в нём, пока не произойдёт пре- рывание. При прерывании по переполнению таймера/счётчика 0 микроконтрол- лер выходит из спящего режима, и программа переходит к обработке прерывания.
588 Глава 7. Примеры проектов При этом вызывается функция обработки прерывания counter, которая корректи- рует текущие значения времени и даты. Корректировка текущего значения даты делается с учётом високосного года. Для этого из функции counter вызывается функция notjeap, которая возвращает О, если текущий год високосный. В противном случае функция notjeap возвра- щает ненулевое значение. Значение текущего времени функция counter выводит в регистр PORTx Порта В и светодиоды, подключённые к выводам Порта В, отображают это значение. После этого выполнение программы возвращается в бесконечный цикл в ос- новной функции main, в то место, откуда она была вызвана. Микроконтроллер снова переводится в спящий режим и находится в нём, пока не произойдёт следу- ющее прерывание, и т. д. Результаты выполнения этой программы представлены на Рис. 7.71 и 7.72. Светодиод VD1, подключённый к самому младшему биту Порта В (РВО), ми- гает каждую секунду: нечётное значение секунд — светодиод горит, чётное — не горит. Следующие шесть светодиодов VD2...VD7 (подключённые к РВ1...РВ6) отображают текущее значение минут в двоичном коде, причём VD2 является са- мым младшим битом, a VD7 — самым старшим. Горящий светодиод соответствует единице в данном бите, а погашенный — нулю. Последний светодиод VD8, под- ключённый к самому старшему биту Порта В (РВ7), отображает текущее значе- ние часов: нечётное значение часов — светодиод горит, чётное — не горит. При текущем значении времени 20 ч 38 мин 18 сек светодиод VD1 будет вы- ключен (значение секунд чётное), светодиоды VD2...VD7 будут отображать дво- ичное число 100110 (38 = 0Ь 0010 0110), a VD8 будет выключен, т. к. значение ча- сов чётное (Рис. 7.71). VCC R1 10 кОм 20:38:18 Q1 4.0 Мгц 24 21 .22. • С1 27 нФ GND • С2 27 нФ C3 0.1 мкФ 0В VCC > GND PFO/ADCO PF1/ADC1 PF2/ADC2 PF3/ADC3 PF4/ADC4 PF5/ADC5 PF6/ADC6 PF7/ADC7 AVCC AREF AGND VD1 R2 470 21,52 § R3 470 VCC XTAL1 XTAL2 TOSC1 TOSC2 PB0/SS PB1/SCK PB2/MOSI PB3/MISO РВ4/0С0 РВ5/ОС1А РВ6/ОС1В РВ7/ОС2 PE0/RXD PE1/TXD РЕ2/АС* РЕЗ/АС- PE4/1NT4 PE5/INT5 PE6/INT6 PE7/1NT7 ALE WR RD RESET PEN PDO/INTO PD1/INT1 PD2/INT2 PD3/INT3 PD4/1C1 PD5 PD6/T1 PO7/T2 ADJ0...7J A(8... 15) DD1 ATmega103 VD2 VD3 VD4 R5 470 VD5 R6 470 VD6 R7 470 VD7 R8 470 VD8 R9 470 sb «е Рис. 7.71. Результат работы программы при текущем значении времени 20 ч 38 мин 18 сек.
7.14. Проект «AVR134» 589 При текущем значении времени 01 ч 19 мин 03 сек светодиод VD1 будет вклю- чён (значение секунд нечётное), светодиоды VD2...VD7 будут отображать двоич- ное число 010011 (19 = 0Ь 0001 0011), a VD8 будет включён, т. к. значение часов нечётное (Рис. 7.72). vcc Q1 4.0 Мгц С1 27 нФ GND 01:19:03 R1 10 кОм 21 23 VCC GND XTAL1 XTAL2 AVCC AREF AGND PF0/ADC0 PF1/ADC1 PF2 ADC2 PF3/ADC3 PF4/ADC4 PF5/ADC5 PF6/ADC6 PF7/ADC7 RESET PEN PDO/INTO PD1/INT1 PD2/INT2 PD3/INT3 PD4/IC1 PD5 PD6/T1 РО7Д2 • С2 27 нФ C3 0.1 мкФ 5 В < ов «а DD1 2i 52 АТтедаЮЗ TOSC1 TOSC2 АО(0...7] А[8. .15] PBO/SS PB1/SCK PB2/MOSI PB3/MISO РВ4/ОСО РВ5/ОС1А РВ6/ОС1В РВ7/ОС2 PEO/RXD PE1/TXD РЕ2/АО РЕЗ/АС- PE4/1NT4 PE5/1NT5 PE6/INT6 PE7/INT7 ALE WR RD Q2 32.768 кГц R3 VCC 4 8 Puc. 7.72. Результат работы программы при текущем значении времени 01 ч 19 мин 03 сек. При отсутствии отладочной платы STK300 можно самостоятельно реализо- вать этот проект по приведённой схеме.
ИСТОЧНИКИ ИНФОРМАЦИИ Сайт разработчика программы CodeVisionAVR — компании HP Info Tech S.R.L.: http://www.hpinfotech.ro. Сайт производителя микроконтроллеров AVR — компании Atmel: www.atmel.com. Русскоязычный сайт компании Atmel: http://www.atmel.ru. Сайт разработчика программы Visual Micro Lab — компании Advanced Micro Tools: http://www.amctools.com/. Сайт разработчика программы Proteus VSM — компании Labcenter Electronics: http://www.labcenter.co.uk/. Сайт, посвященный протоколу 1-Wire: http://www.elin.ru/l-Wire/m_lan.htm. Сайт производителя электронных компонент — компании Dallas Semiconductor: http://www.dalsemi.com/. Сайт производителя электронных компонент — компании Analog Devices: http://www.analog.com/. Сайт производителя электронных компонент — компании National Semiconductor: http://www.national.com. Сайт производителя электронных компонент — компании Philips: http://www.semiconductors.philips.com. Сайт производителя электронных компонент — компании Maxim Integrated Products — http://www.maxim-ic.com. Сайт производителя LCD-дисплеев — компании МЭЛТ http://www.melt.aha.ru/. Сайт производителя LCD дисплеев — компании Data Vision: http://www.datavision.com.tw/. Сайт разработчика программы Total Commander: http://www.ghisler.com/. Гребнев, В.В. Микроконтроллеры семейства AVR фирмы Atmel. — М.: ИП Ра- диоСофт, 2002. Голубцов, М.С. Микроконтроллеры AVR: от простого к сложному. — М.: СО- ЛОН-Пресс, 2003. Керниган, Б. Язык программирования Си / Б. Керниган, Д. Ригли. — М.: Ра- дио и связь, 1992. Atmel Corporation 8-bit RISC Microcontrollers Data Book. HP Info Tech S.R.L. CodeVisionAVR. Version 1.24.1. User Manual. Система помощи программы AVR Studio от Atmel. Система помощи программы CodeVisionAVR от HP Info Tech S.R.L.
Лебедев Михаил Борисович CodeVisionAVR. Пособие для начинающих Главный редактор В. М. Халикеев Ответственный редактор Н. С. Мухарева Технический редактор Н. В. Тищенко Верстальщик И. С. Каинова Подписано в печать 19.03.2008. Формат 70x100/16. Бумага офсетная. Гарнитура «NewtonC». Печать офсетная. Объем 37 п. л. Усл. п. л. 48 Тираж 1500 экз. Код CODVIS. Заказ № 123 Издательский дом «Додэка-ХХ1» ОКП 95 3000 105318 Москва, а/я 70 Тел./факс: (495) 366-24-29, 366-09-22 E-mail: books@dodeca.ru; red@dodeca.ru Отпечатано с готовых диапозитивов в ОАО «Московская типография № 6 115088 Москва, Южнопортовая ул., 24
Издательский дом «Додэка-ХХ1» приглашает к сотрудничеству: авторов, переводчиков, редакторов Подробности — www.dodeca.ru УМНЫЕ КНИГИ РОКАВСКЖ «Дока-букс» — это динамично развивающаяся компания, занимающаяся распространением технической литературы . в России и ближнем зарубежье. Фирма основана группой компаний «Симметрон» на базе книготоргового отдела Издательского дома «Додэка-XXI» в начале 2006 г. и является эксклюзивным представителем данного издательства. В ассортименте «Дока-букс» книги более 80 издательств. Приглашаем к сотрудничеству книготорговые компании и издательства. Интернет-магазин — www.dokabooks.ru тел./факс: +7(495) 366-2429,366-0922 e-mail: books@dodeca.ru
CodeVisionAVR CodeVision AVR Пособие для начинающих В книге изложены основные приёмы работы в интегрированной среде разра- ботки CodeVisionAVR, предназначенной для разработки программного обеспече- ния и программирования микроконтрол- леров AVR на языке Си. Автор постарался сделать описание программы CodeVisionAVR максимально понятным: приводят- ся переводы всех меню и команд меню, диалоговых окон, а также различного рода предупреждений. Кроме того, для облегчения восприятия материала книга богато иллюстриро- вана и снабжена перекрёстными ссылками. Книга рассчитана на читателей, изучающих основы микро- контроллерной техники, и может быть полезна студентам ву- зов соответствующих специальностей. Новинки серии «Программируемые системы»