Text
                    Программирование
на языке С для AVR и PIC
микроконтроллеров
Scanned бу M&Pic
“МК-Пресс’
Киев,2006

ББК 32.973-04 Ш83 УДК 004.312 LU83 Программирование на языке С для AVR и PIC микроконтроллеров./ Сост. Ю.А. Шпак— К.: “МК-Пресс”, 2006. —400 с., ил. ISBN 966-8806-16-6 В книге рассмотрено программирование на языке С микроконтроллеров AVR с использованием компилятора WinAVR, а также микроконтроллеров PIC с использованием компилятора CCS-PICC. Кратко рассмотрена архитектура и аппаратное обеспечение микроконтроллеров AVR и PIC. Дано описание средств программной разработки в среде WinAVR и CCS-PICC, включая эмуля- цию программ с помощью AVR Studio и MPLAB. Кратко рассмотрен стандарт- ный синтаксис языка С и директивы препроцессора, а также особенности про- граммирования на этом языке для микроконтроллеров. Книга содержит множе- ство программных примеров на С, а также справочник с описанием системы ас- семблерных команд микроконтроллеров AVR и PIC. ББК 32.973-04 Программирование на языке С для AVR и PIC микроконтроллеров Подписано в печать 25.04.2006. Формат 70 х 100 1/16. Бумага офсетная. Печать офсетная. Усл. печ. л. 32,4. Уч.-изд. л 16,2. Тираж 2500 экз. Заказ 6-767 СПД Савченко Л.А., Украина, г.Киев, тел./ф.: (044) 517-73-77; e-mail: info@mk-press.com. Свидетельство о внесении субъекта издательского дела в Государственный реестр издателей, производителей и распространителей издательской продукции: серия ДК №51582 от 28.11.2003г. Отпечатано в ЗАО "ВИПОЛ". 03151, г.Киев, ул. Волынская, 60 ISBN 966-8806-16-6 © "МК-Пресс”, 2006
Краткое оглавление 3 Краткое оглавление Глава 1. Архитектура микроконтроллеров AVR и PIC................22 Память........................................................22 Обработка прерываний..........................................28 Сброс.........................................................35 “Спящие” режимы процессора....................................36 Таймеры/счетчики..............................................36 Сторожевой таймер.............................................49 Параллельные порты ввода/вывода...............................51 Последовательный ввод/вывод...................................53 Аналого-цифровое преобразование...............................69 Глава 2. Компиляторы и средства разработки......................74 Программное обеспечение для микроконтроллеров AVR.............74 Программное обеспечение для микроконтроллеров Р1С.............86 Программирование целевого устройства.........................105 Глава 3. Язык С и директивы препроцессора......................107 Вводные понятия..............................................107 Структура программы на С................................... 1 10 Типы данных, переменные, константы.......................... 111 Функции.......................................................117 Структуры....................................................120 Указатели и адреса переменных................................121 Массивы и строки.............................................124 Операторы ветвления..........................................126 Циклические конструкции......................................127 Стандартные функции ввода/вывода.............................130 Директивы препроцессора......................................134 Обработка прерываний.........................................145 Исполнение ассемблерного кода................................149 Глава 4. Программные примеры для микроконтроллеров AVR.........156 Вывод некоторого числа по нажатию кнопки.....................156 Бегающие “глаза”.............................................157 Индикатор, мигающий каждую секунду...........................158 Переключение индикаторов по нажатию кнопки...................159 Прием символа от ПК через интерфейс RS-232 и передача в ответ строки с помощью UART........................................160 Светофор, управляемый напряжениями разного уровня на аналоговом входе микроконтроллера............................162 Обмен данными по интерфейсу SP1..............................163
4 Управление яркостью свечения светодиода с помощью широтно-импульсной модуляции.................................164 Измерение ширины импульсов...................................168 Измерение скорости вращения двигателя с отображением результата на ЖК-дисплее................................................169 Контроль скорости вращения вентилятора с учетом показаний датчика температуры......................................... 174 Проект "Часы реального времени”............................. 180 Глава 5. Программные примеры доя микроконтроллеров PIC.........195 Отображение состояния выводов порта......................... 195 Управление частотой мерцания светодиодов с помощью различных таймеров.....................................................196 Управление светофорами на перекрестке........................197 Секундомер...................................................201 Обмен данными в режиме PSP...................................203 Контроль предельной Скорости вращения двигателя..............204 Обмен данными по шине CAN....................................206 Обмен данными по интерфейсу 12С............................ 221 Индикация уровня напряжения на аналоговом входе..............222 Приложение А. Таблица символов ASCII...........................224 Приложение Б. Преобразование из одной системы счисления в другую 225 Приложение В. Система команд микроконтроллеров AVR.............228 Алфавитный перечень команд...................................229 Команды по категориям........................................258 Приложение Г. Система команд микроконтроллеров PIC.............264 Алфавитный перечень команд...................................264 Команды по категориям........................................285 Приложение Д. Библиотечные функции и макроопределения..........289 Стандартные функции..........................................289 Функции и макроопределения компилятора WinAVR................306 Функции и макроопределения компилятора CCS-P1CC..............340
Содержание 5 Содержание Введение...............................................................20 Глава 1. Архитектура микроконтроллеров AVR и PIC...........................22 Память................................................................22 Память данных......................................................23 Регистры общего назначения..........................:.............23 Регистры специальных функций микроконтроллеров PIC...............24 Область ввода/вывода микроконтроллеров AVR.......................24 Регистр состояния SREG микроконтроллеров AVR.....................25 Внутренняя и внешняя память SRAM микроконтроллеров AVR...........26 Стек.............................................................26 Память программ....................................................27 Память ЕЕРROM микроконтроллеров A VR...............................27 Запись в память EEPROM...........................................28 Чтение из памяти EEPROM..........................................28 Обработка прерываний..................................................28 Управление прерываниями в микроконтроллерах AVR....................30 Регистр GIMSK....................................................30 Регистр GIFR......................................................3 1 Регистры TIMSK и TIFR............................................31 Управление прерываниями в микроконтроллерах Р1С....................32 Микроконтроллеры серии Р1С17Сх...................................32 Микроконтроллеры серии Р1С18Сх...................................33 Сброс.................................................................35 “Спящие” режимы процессора............................................36 Таймеры/счетчики......................................................36 Таймеры/счетчики микроконтроллеров AVR.............................37 Т/СО.............................................................38 Т/С1.............................................................39 Т/С2.............................................................43 Таймеры/счетчики микроконтроллеров PIC.............................44 TMR0.............................................................44 TMR1 ............................................................45 TMR2............................................................ 47 Модуль ССР.......................................................48 Сторожевой таймер.....................................................49 Параллельные порты ввода/вывода.......................................51 Режим PSP порта D микроконтроллеров PIC............................52 Последовательный ввод/вывод...........................................53 Приемопередатчик UARTмикроконтроллеров A VR........................53 Настройка скорости передачи данных через UART....................55 Приемопередатчик ESARTмикроконтроллеров PIC........................56 Синхронная передача данных по интерфейсу SPI.......................57 Регистры управления и состояния SPI в микроконтроллерах AVR......59 Регистры управления и состояния SPI в микроконтроллерах PIC......62 Синхронная передача данных по интерфейсу 12С.......................63
6 Протокол шины 12С................................................64 Адресация ведомых устройств......................................66 Работа с интерфейсом Г’С в микроконтроллерах Р1С.................67 Модуль шины CAN.....................................................68 Аналого-цифровое преобразование.......................................69 Встроенный аналоговый компаратор....................................77 Глава 2. Компиляторы и средства разработки..................................74 Программное обеспечение для микроконтроллеров AVR.....................74 Разработка и компиляция программ в Programmers Notepad..............74 Эмуляция и отладка программ в среде A VR Studio 4...................78 Окно Workspace...................................................81 Окна Memory......................................................82 Окно Register....................................................83 Окно Watch.......................................................84 Отладка программы................................................84 Настройка параметров имитатора...................................85 Программное обеспечение для микроконтроллеров PIC.....................86 Разработки и компиляция программ в среде CCS-PJCC...................86 Создание проектов CCS-PICC вручную...............................86 Создание проектов CCS-P1CC с помощью PIC Wizard..................89 Открытие и добавление в проект файлов с исходным кодом...........95 Компиляция проекта...............................................95 Меню Tools.......................................................96 Эмуляция г/ отладка программ в среде Microchip MPLAB 7.31...........97 Рабочая область и проект MPLAB...................................98 Компиляция под управлением MPLAB............................... 100 Настройка режима отладки/эмуляции...............................100 Работа в режиме отладки/эмуляции................................102 Окна отладчика..................................................103 Программирование целевого устройства.................................105 Программирование устройства в среде A VR Studio 4..................105 Программирование устройств в среде CCS-P1CC........................106 Программирование устройств в среде MPLAB...........................106 Глава 3. Язык С и директивы препроцессора..................................107 Вводные понятия......................................................107 Структура программы на С.............................................110 Типы данных, переменные, константы...................................111 Правила преобразований из одной системы счисления в другую.........112 Тип char...........................................................113 Пользовательские типы..............................................113 Переменные.........................................................114 Область видимости переменных....................................114 Константы..........................................................115 Перечислимые типы...............................................116 Приведение типов...................................................116 Оператор sizeof....................................................116
Содержание 7 Функции............................................................117 Возвращаемые значения............................................118 Прототипы функций................................................118 Классы памяти при объявлении локальных переменных................119 Рекурсия.........................................................119 Структуры..........................................................120 Указатели и адреса переменных......................................121 Передача в функции параметров по ссылке..........................122 Указатели на структуры...........................................123 Массивы и строки...................................................124 Строки...........................................................125 Многомерные массивы..............................................125 Операторы ветвления................................................126 Оператор if-else.................................................126 Условные выражения............................................ 126 Оператор swi tch-case............................................127 Циклические конструкции............................................127 Конструкция while................................................128 Конструкция for..................................................128 Конструкция do-while.............................................129 Организация бесконечных циклов...................................129 Операторы break и con cinue......................................129 Стандартные функции ввода/вывода...................................130 Ввод/вывод символов с помощью функций getchar () иputchar ().....130 Функции вывода строк ри ts () и print f ().......................131 Функции ввода строк gets () и scanf () ..........................133 Директивы препроцессора............................................134 Директива #include...............................................134 Директива lldefine...............................................134 Директивы условной компиляции....................................136 Директива #еггог.................................................138 Директивы, характерные для компилятора CCS-PICC..................138 Директива #bit.................................................138 Директива #byte............................................... 138 Директива #сазе................................................138 Директива #device..............................................139 Директива #fuse............................................... 139 Директива #locate............................................. 141 Директива #org.................................................141 Директива #opt................................................ 142 Директива #priority........................................... 142 Директива #reserve.............................................142 Директива #rom................................................ 142 Директива #type............................................... 143 Директива #use delay...........................................143 Директивы #use xxx_io......................................... 143 Директива #use i2c.............................................144 Директива #use rs232 ..........................................144
8 Директива #zero__ram........................................145 Обработка прерываний............................................145 Обработка прерываний в среде WinA YR..........................145 Обработка прерываний в среде CCS-P1CC.........................148 Исполнение ассемблерного кода...................................149 Использование ассемблера в компиляторе WinA VR................149 Ассемблерный код........................................... 150 Входные и выходные операнды................................ 150 Резервирование регистров................................... 152 Использование ассемблера в компиляторе CCS-P1CC...............154 Глава 4. Программные примеры для микроконтроллеров AVR................156 Вывод НЕКОТОРОГО числа по нажатию кнопки........................156 Бегающие “глаза”................................................157 Индикатор, мигающий каждую секунду..............................158 Переключение индикаторов по нажатию кнопки......................159 Прием символа от ПК через интерфейс RS-232 и передача в ответ строки с помощью UART..................................................160 Светофор, управляемый напряжениями разного уровня на аналоговом входе микроконтроллера..........................................162 Обмен данными по интерфейсу SPI.................................163 Управление яркостью свечения светодиода с помощью широтно-импульсной модуляции....................................164 Измерение ширины импульсов......................................168 Измерение скорости вращения двигателя с отображением результата на ЖК-дисплее......................................................169 Контроль скорости вращения вентилятора с учетом показаний датчика температуры.............................................174 Проект “Часы реального времени”..................................180 Функция калибрования генератора синхроимпульсов...............180 Размещение строк во флэш-памяти...............................183 Преобразование времени, выраженного в секундах, в понятную форму с помощью двоично-десятичной арифметики.......................184 Программная реализация часов реального времени................185 Глава 5. Программные примеры для микроконтроллеров PIC................195 Отображение состояния выводов порта.............................195 Управление частотой мерцания светодиодов с помощью различных таймеров..............................................196 Управление светофорами на перекрестке...........................197 Секундомер......................................................201 Обмен данными в режиме PSP......................................203 Контроль предельной скорости вращения двигателя.................204 Обмен данными по цзине CAN......................................206 Обмен данными по интерфейсу 12С.................................221 Индикация уровня напряжения на аналоговом входе.................222 Приложение л. Таблица символов ASCII..................................224
Содержание 9 Приложение Б. Преобразование из одной системы счисления в другую.225 Приложение В. Система команд микроконтроллеров AVR...............228 Алфавитный перечень команд..................................229 A DC.......................................................229 ADD........................................................229 ADIW.......................................................229 AND........................................................229 ANDI.......................................................230 A SR.......................................................230 BCLR.......................................................230 BLD........................................................230 BRBC.......................................................23! BRBS.......................................................231 BRCC.......................................................231 BROS.......................................................231 BREQ.......................................................232 BRGE.......................................................232 BRHC.......................................................232 BRHS.......................................................232 В RID......................................................233 BRIE.......................................................233 BRLO.......................................................233 BRLT.......................................................233 BRMI.......................................................234 BRNE.......................................................234 BRPL.......................................................234 BRSH.......................................................235 BRTC.......................................................235 BRTS.......................................................235 BRVC.......................................................235 В RES......................................................236 BSET.......................................................236 BST........................................................236 CALL.......................................................236 CB1........................................................237 CBR........................................................237 CLC........................................................23n CLH........................................................237 CLI........................................................237 CLN........................................................238 CLR........................................................238 CLS........................................................238 CLT........................................................238 CLV........................................................239 CLZ........................................................239 COM........................................................239 CP.........................................................239
10 CPC..........................................................239 CPI..........................................................240 CPSE.........................................................240 DEC..........................................................240 ELPM.........................................................241 EOR..........................................................241 FMUL.........................................................241 FMULS........................................................241 FMULSU.......................................................242 ICALL........................................................242 1JMP.........................................................242 IN...........................................................242 INC..........................................................243 .IMP.........................................................243 LD (LDD).....................................................243 Косвенная загрузка из SRAM в регистр с помощью указателя X.243 Косвенная загрузка из SRAM в регистр с помощью указателя Y.244 Косвенная загрузка из SRAM в регистр с помощью указателя Z.244 LD1..........................................................245 LDS..........................................................245 LPM..........................................................245 LSL..........................................................245 LSR..........................................................246 MOV..........................................................246 МО VW........................................................246 MUL..........................................................246 MULS.........................................................247 MULSU........................................................247 NEG..........................................................247 NOP..........................................................247 OR...........................................................248 OR1..........................................................248 OUT..........................................................248 POP..........................................................248 PUSH.........................................................249 RCALL........................................................249 RET..........................................................249 RET1.........................................................249 R.IMP........................................................250 ROL..........................................................250 ROR..........................................................250 SBC..........................................................250 SBC I........................................................251 SB1..........................................................251 SB1C.........................................................251 SB IS........................................................251 SB/W.........................................................252 SBR..........................................................252
Содержание 11 SBRC...............................................................................252 SB RS..............................................................................252 SEC................................................................................253 SEH................................................................................253 SEI................................................................................253 SEN................................................................................253 SER................................................................................253 SES................................................................................254 SET................................................................................254 SEV................................................................................254 SEZ................................................................................254 SLEEP..............................................................................254 SPM................................................................................255 ST (STD)...........................................................................255 Косвенное сохранение содержимого регистра в SRAM с помощью указателя X..........255 Косвенное сохранение содержимого регистра в SRAM с помощью указателя Y..........255 Косвенное сохранение содержимого регистра в SRAM с помощью указателя Z..........256 STS................................................................................256 SUB................................................................................257 SUBl...............................................................................257 SWAP...............................................................................257 TST................................................................................257 WDR................................................................................257 Команды по категориям.................................................................258 Приложение Г. Система команд микроконтроллеров PIC...........................................264 Алфавитный перечень команд............................................................264 ADDLW..............................................................................264 ADDWF..............................................................................265 ADDWFC.............................................................................265 ANDLW..............................................................................265 ANDWF..............................................................................265 ВС.................................................................................266 BCF................................................................................266 BN.................................................................................266 BNC................................................................................266 BNN............................................................................................................................266 В NO У.............................................................................267 BNZ................................................................................267 BOV................................................................................267 BRA................................................................................267 BSF................................................................................267 BTFSC........................................................................................................................268 BTFSS..............................................................................268 BTG................................................................................268 BZ.................................................................................268 CALL...............................................................................269 CLRF...............................................................................269
12 269 270 270 270 270 271 271 271 271 272 272 272 272 273 273 CI.RII'................................................... CLRWDT.................................................... COMF...................................................... CPFSEQ.................................................... CPFSGT.................................................... CPFSLT.................................................... DA If .................................................... DCFSNZ.................................................... DECF...................................................... DECFSZ.................................................... GOTO...................................................... INCE...................................................... 1NCFSZ.................................................... INFSNZ.................................................... IORL II'.................................................. 1ORUF................................................273 LC.ILL...............................................274 LFSR.................................................274 MOVF.................................................274 MOVFF................................................274 MOVFP................................................274 MOV LB..........................................275 MOVLR...........................................275 MOVLW......................................... 275 MOV PF..........................................275 MOVWF...........................................275 MULLW...........................................276 MULWF...........................................276 NEGF............................................276 NEGW............................................276 NOP.............................................277 OPTION................................................277 POP...................................................277 PUSH..................................................277 RCALL.................................................277 RESET.................................................278 RETFIE................................................278 RETLW.................................................278 RETURN................................................278 RLCF..................................................279 RLE...................................................279 RLNCF.................................................279 RRCF..................................................279 RRF...................................................280 RRNCF.................................................280 SETF..................................................280 SLEEP.................................................281 SI IBFWВ..............................................281
Содержание 13 SUBLW........................................................281 SUBWF........................................................281 SUBWFB.......................................................282 SWA PF.......................................................282 TABLRD.......................................................282 TABLWT.......................................................283 TLRD.........................................................283 TLWT.........................................................283 TSTFSZ.......................................................284 TRIS.........................................................284 XORLW........................................................284 XORWF........................................................284 Команды no категориям..........................................285 Приложение Д. Библиотечные функции и макроопределения................289 СТАНДАРТ! 1ЫЕ ФУНКЦИИ..........................................289 Математические...............................................289 abs.........................................................289 acos........................................................289 asin........................................................289 atari.......................................................289 atan2.......................................................290 ceil .......................................................290 cosh........................................................290 div.........................................................290 exp.........................................................290 fabs........................................................290 floor.......................................................291 fmod........................................................291 frexp.......................................................291 labs .......................................................291 Idexp.......................................................291 Idiv........................................................292 log.........................................................292 loglO.......................................................292 modf........................................................292 pow.........................................................292 sin.........................................................292 sinh........................................................293 sqrt........................................................293 tan....................................:....................293 tanh........................................................293 Для работы co строками........................................ 293 atoi........................................................293 atol .......................................................294 sprintf.....................................................294 strcat......................................................294 strehr......................................................295
14 strcmp......................................................295 strcpy......................................................295 strlen......................................................296 strlwr......................................................296 strncmp.....................................................296 strncpy.....................................................297 strrchr.....................................................297 strstr......................................................297 strtod......................................................298 strtol......................................................298 strtoul.....................................................299 Для работы с символами.........................................299 isalnum.....................................................299 isalpha.....................................................299 iscntrl.....................................................300 isdigit.....................................................300 isgraph.....................................................300 islower.....................................................301 isprint.....................................................301 ispunct.....................................................302 isspace.....................................................302 isupper.....................................................302 isxdigit....................................................303 tolower.....................................................303 toupper.....................................................303 Для работы co случайными числами...............................303 rand........................................................303 srand.......................................................304 Для работы с памятью...........................................304 memchr......................................................304 memcmp......................................................304 memcpy......................................................305 memmove.....................................................305 memset......................................................306 Функции и макроопределения компилятора WinAVR...................306 Математические макроопределения................................306 М_Р1........................................................306 M_SQRT2.....................................................306 RAND_ MAX...................................................306 RANDOM__MAX.................................................306 Математические функции.........................................306 inverse.....................................................306 isinf.......................................................307 square......................................................307 Функции для работы co строками.................................307 dtostre.....................................................307 dtostrf.....................................................308 itoa........................................................308
Содержание 15 Itoa............................................................308 snprintf........................................................309 snprintf—P......................................................309 sprintf__P......................................................309 strcasecmp......................................................310 strlcat.........................................................310 strlcpy.........................................................311 strncasecmp.....................................................311 strncat.........................................................311 strnlen.........................................................312 strrev..........................................................312 strsep..........................................................312 strtok_r........................................................313 strupr..........................................................314 ultoa...........................................................314 utoa............................................................314 Макроопределения для работы co строками, хранимыми во флэш-памяти.315 PGM_P...........................................................315 pgm_read_by te..................................................315 pgm_read_byte_far...............................................315 pgm_read_byte_near..............................................315 pgm_read_word...................................................315 pgm_read_word_f ar..............................................315 pgm_read__word__near............................................316 PGM_VOID__P.....................................................316 PSTR............................................................316 Функции для работы co строками, хранимыми во флэш-памяти..........316 memcpy_P........................................................316 strcasecmp_P....................................................317 strcat_P........................................................317 strcmp_P........................................................318 strcpy_P........................................................3 18 strlcat_P.......................................................318 strlcpy_P.......................................................319 strlen_P........................................................319 strncasecmp__P..................................................320 strncat_P.......................................................320 strncmp_P.......................................................320 strncpy_P.......................................................321 Функции для работы с символами....................................321 isascii.........................................................321 isblank.........................................................322 toasc ii........................................................322 Макроопределения для организации ввода/вывода.....................322 FILE............................................................322 getc............................................................323 getchar.........................................................323 put с...........................................................323
16 putchar.......................................................323 stderr........................................................323 stdin.........................................................323 stdout........................................................323 Функции ввода/вывода............................................324 clearerr......................................................324 f close.......................................................324 f devopen.....................................................324 f eo f........................................................325 fer ror.......................................................325 fgetc.........................................................325 rgets.........................................................326 fprintf.......................................................326 fprintf_P.....................................................326 fputc.........................................................326 fputs.........................................................327 f puts_P.....................................................327 f read...................................................... 327 f scant......................................................327 f scant .....................................................328 f write......................................................328 gets ........................................................328 print!........................................................328 printf_P......................................................329 puts ........................................................329 puts__P.......................................................329 scant.........................................................329 scantpJ.......................................................330 sscanf........................................................330 sscanf _P.....................................................330 Функции управления микроконтроллером............................33 / abo rt........................................................33 1 _delay_loop_l.................................................33 1 delay_loop_2.................................................331 exit..........................................................331 set_sleep_mode................................................33 1 s 1 eepjnode..................................................332 Функции для работы с таймерами микроконтроллера.................332 timerO_source.................................................332 timerO__stop..................................................332 timer0_s tart.................................................332 t imer_enab le___in t.........................................333 Макроопределения для работы co сторожевым таймером..............333 wdt_disable...................................................333 wdt^enable....................................................333 wdt_reset.....................................................334 wdt_write....................................................334 Функции для работы co случайными числами........................334
Содержание 17 random........................................................334 srandom.......................................................334 Функции для работы с памятью....................................335 cal loo.......................................................335 free..........................................................335 malloc........................................................335 memccpy.......................................................335 Макроопределения для обработки прерываний.......................336 cli...........................................................336 EMPTY_INTERRUPT...............................................336 INTERRUPT.....................................................336 sei...........................................................336 SIGNAL........................................................336 Макроопределения для работы с разрядами регистров...............336 __BV..........................................................336 bit_is_clear..................................................337 bit_is_set....................................................337 loop_until_bit_is_clear.......................................337 loop_un til_bi t_i s_se t.....................................337 Макроопределения для работы с памятью EEPROM....................337 eeprom_busy_wait..............................................337 eeprom_is_ready...............................................337 Функции для работы с памятью EEPROM.............................338 eeprom_read block.............................................338 eeprom_read_byte..............................................338 eeprom_read__word.............................................338 eeprom_write_block............................................339 eeprom_write__byte............................................339 eeprom_write_word.............................................339 Функции и макроопределения компилятора CCS-PICC..................340 Матем атические макроопределения................................340 LN2...........................................................340 LN10..........................................................340 PI............................................................340 PI_DIV__BY_TWO................................................340 RAND_MAX......................................................340 SQRT2.........................................................340 TWOBYPI.......................................................340 Функции для работы со строками..................................340 atof..........................................................340 atoi32........................................................341 strcspn.......................................................341 stricmp.......................................................342 strpbrk.......................................................342 strspn........................................................343 strtok........................................................343 Функции ввода/вывода............................................344 assert........................................................344 2 — 6-767
18 fgetc........................................................344 f gets.......................................................345 fprint f.....................................................345 fputc........................................................346 fputs........................................................346 getc.........................................................347 gets.........................................................347 input........................................................348 inpu t_X.....................................................348 kbhit........................................................349 output_bit...................................................349 output_float.................................................350 output_high..................................................350 output—low...................................................351 output—X.....................................................352 port—b_pullups...............................................352 printf.......................................................352 putc.........................................................353 puts.........................................................353 set_tris_X...................................................354 set—uart_speed...............................................354 sprintf......................................................355 Функции управления микроконтроллером...........................356 delay_cycles.................................................356 delay_ms.....................................................356 delay_us.....................................................357 disable_interrupts...........................................358 enable_interrupts............................................359 ext_int_ edge................................................360 reset_cpu....................................................361 restart—cause................................................361 sleep........................................................362 Функции для работы с таймерами и модулем ССР...................363 get_rtcc.....................................................363 get—timerX...................................................363 restart—wdt..................................................364 set—pwmX—duty................................................365 set—rtcc.....................................................365 set—timerX...................................................366 setup—ccpX...................................................366 setup—timet—X................................................367 setup_wdt....................................................369 Функции для работы с разрядами и памятью.......................370 bit_clear....................................................370 bit—set......................................................370 bit—test.....................................................371 input........................................................371 makelfi......................................................372
Содержание 19 make32......................................................372 таке8.......................................................373 of f setof..................................................373 offsetofbit.................................................374 read_bank...................................................374 rotate_left.................................................375 rotate_right................................................375 shift—left..................................................376 shift—right.................................................376 swap........................................................377 write_bank..................................................377 Функции для работы с памятью EEPROM...........................378 read—eeprom.................................................378 write_eeprom.............................................. 379 read—program—eeprom.........................................379 write_program_eeprom........................................380 Функции для работы с интерфейсом SPI..........................381 setup—spi..................................................'381 spi_data_iS—in..............................................382 spi_read....................................................382 spi_write...................................................383 Функции для работы с интерфейсом PSP..........................384 psp_input—full..............................................384 psp_output—full.............................................384 psP—overflow................................................385 setup—psp...................................................385 Функции для работы с интерфейсом fC...........................386 i2C—pol 1...................................................386 i2c_read....................................................386 i2c_start...................................................387 i2c_stop....................................................388 i2c_write...................................................388 Функции для работы с аналоговыми сигналами....................389 read—ado....................................................389 set—adc_channel.............................................390 setup—adc...................................................390 setup_adc_ports.............................................391 setup_comparator............................................392 2*
20 Введение Однокристальные микроконтроллеры находят широкое применение в самых разнообразных сферах: от измерительных приборов, фотоаппаратов и видеокамер, принтеров, сканеров и копировальных аппаратов до изделий электронных развле- чений и всевозможной домашней техники. Со времени появления первых микропроцессоров в 1970-х годах их слож- ность постоянно возрастала за счет появления новых аппаратных решений и до- бавления новых команд, предназначенных для решения новых задач. Так посте- пенно сложилась архитектура, получившая впоследствии название CISC (Complex Instruction Set Computers — компьютеры co сложным набором команд). В даль- нейшем обозначилось и нашло активное развитие еще одно направление: архитек- тура RISC (Reduced Instruction Set Computers — компьютеры с сокращенным на- бором команд). Именно к этой архитектуре относятся микроконтроллеры AVR от компании Atmel и PIC от компании Microchip, которым посвящена эта книга. Основное преимущество RISC-процессоров заключается в том, что они про- сты, выполняют ограниченный набор команд, и, как следствие, очень быстродей- ствующие. Это позволяет снизить стоимость и сложность их программирования. Обратной стороной RISC-архитектуры стала необходимость создания допол- нительных команд на ассемблере, которые у CISC-устройств реализованы в аппа- ратной части. Например, вместо того, чтобы просто вызвать команду деления, ко- торая характерна для устройств CISC, разработчику, имеющему дело с RISC- процессором, приходится применять несколько последовательных команд вычи- тания. Однако подобный недостаток с лихвой компенсируется ценой и скоростью работы RISC-устройств. Кроме того, если создавать программы на языке С, то по- добные проблемы вообще перестают иметь какое-либо значение для разработчи- ка, поскольку они решаются компилятором, который автоматически генерирует весь недостающий ассемблерный код. На заре возникновения микропроцессоров разработка программного обеспе- чения происходила исключительно на том или ином языке ассемблера, ориенти- рованном на конкретное устройство. По сути, такие языки представляли собой символьные мнемоники соответствующих машинных кодов, а перевод мнемоники в машинный код выполнялся транслятором. Однако главный недостаток ассемб- лерных языков заключается в том, что каждый из них привязан к конкретному ти- пу устройств и логике его работы. Кроме того, ассемблер сложен в освоении, что требует достаточно больших усилий для его изучения, которые, к тому же, оказы- ваются потраченными впустую, если впоследствии потребуется перейти на ис- пользование микроконтроллеров других производителей. Язык С, являясь языком высокого уровня, лишен подобных недостатков и может использоваться для программирования любого микропроцессора, для кото- рого есть компилятор с языка С. В языке С все низкоуровневые операции, выпол- няемые компьютерами, представлены в виде абстрактных конструкций, позво- ляющих разработчикам сосредоточиться на программировании одной лишь логи- ки, не заботясь о машинном коде. Изучив язык С, можно легко переходить от од- ного семейства микроконтроллеров к другому, тратя гораздо меньше времени на разработку.
Введение 21 В этой книге рассмотрены основы программирования на языке С микрокон- троллеров AVR с использованием компилятора WinAVR, а также микроконтрол- леров PIC с использованием компилятора CCS-PICC. Здесь нет “сверхсложных” программных проектов. Скорее, эта книга — полезное справочное пособие с про- стыми примерами, призванное помочь читателю быстро и легко освоить азы про- граммирование микроконтроллеров для конкретных областей применения. ВНИМАНИЕ! Не все из представленных в главах 4 и 5 программных примеров были протестированы в аппаратной реализации, а только в режиме эмуляции. По этой причине, будем благодар- ны читателям, приславшим свои замечания или дополнения. И еще... Приглашаются к со- трудничеству все любители программирования микроконтроллеров на языке С, желающие опубликовать "жемчужины" своего творчества для широкого круга читателей. Желаем успехов в освоении вселенной под названием “Программирование микроконтроллеров”. Надеемся, эта книга станет хорошим подспорьем в преодо- лении нелегкого пути проб и ошибок. Через тернии к звездам...
Глава 1 Архитектура микроконтроллеров AVR и PIC В принципе, все микроконтроллеры построены по одной схеме. Система управления, состоящая из счетчика команд и схемы декодирования, выполняет считывание и декодирование команд из памяти программ, а операционное устрой- ство отвечает за выполнение арифметических и логических операций; интерфейс ввода/вывода позволяет обмениваться данными с периферийными устройствами; и, наконец, необходимо иметь запоминающее устройство для хранения программ и данных (рис. 1.1). Рис. 1.1. Обобщенная структура микроконтроллера В этой книге автор не привязывается к какому-либо конкретному типу микро- контроллеров AVR или PIC, поэтому ниже будут рассмотрены только общие для большинства микроконтроллеров особенности архитектуры памяти, вопросы вво- да/вывода, обработки прерываний, сброса и др. Память В микроконтроллерах AVR и PIC память реализована по Гарвардской архи- тектуре, что подразумевает разделение памяти команд и данных. Это означает, что обращение к командам осуществляется независимо от доступа к данным. Пре- имуществом такой организации является повышение скорости доступа к памяти.
Память 23 К тому же, в микроконтроллерах PIC к памяти данных и к памяти команд можно обращаться фактически одновременно, что еще больше повышает скорость обра- ботки программ. Рассмотрим, какие типы памяти могут использоваться в микро- контроллерах AVR и PIC. Память данных Память данных предназначена для записи/чтения данных, используемых про- граммами. Является энергозависимой, то есть, при отключении питания микро- контроллера все хранимые в ней данные, будут потеряны. В микроконтроллерах AVR память данных имеет более развитую структуру по сравнению с микрокон- троллерами PIC, что показано на рис. 1.2. [ПРИМЕЧАНИЕ Здесь и далее шестнадцатеричные числа будут представлены в форме, принятой в языке С: с префиксом Ох. Адрес 7 Область регистров специальных функций Область регистров общего назначения Рис. 1.2. Структура памяти данных в микроконтроллерах AVR и PIC Область статической памяти SRAM (Static Random Access Memory) обозначе- на на рис. 1.2 пунктиром, поскольку используется не всеми микроконтроллерами AVR (это относится как к внутренней, так и к внешней SRAM). Ее начальный ад- рес — 0x0 60, а верхний адрес — разный в различных устройствах. В некоторых микроконтроллерах AVR можно увеличивать пространство па- мяти SRAM посредством подключения внешних блоков памяти вплоть до 64 Кбайт, однако для этого приходится пожертвовать портами А и С, которые в этом случае применяются для передачи данных и адресов. Регистры общего назначения Область регистров общего назначения (рабочих регистров) предназначена для временного хранения переменных и указателей, используемых процессором для выполнения программ. В микроконтроллерах AVR она состоит из 32 восьмираз- рядных регистров (диапазон адресов 0x000 - 0x01F). В микроконтроллерах PIC
24 Глава 1. Архитектура микроконтроллеров AVR и PIC регистры общего назначения также восьмиразрядные, однако их количество и диапазон адресов зависят от конкретного типа устройства. В программах, написанных на языке С, непосредственное обращение к реги- страм общего назначения обычно не требуется, если только не используются фрагменты на языке ассемблера. Регистры специальных функций микроконтроллеров PIC Регистры специальных функций используются в микроконтроллерах PIC для управления различными операциями. Как и в случае с регистрами общего назна- чения, их количество и адресация отличаются от устройства к устройству. В про- граммах, написанных на языке С, непосредственное обращение к регистрам спе- циальных функций обычно не требуется, если только не используются фрагменты на языке ассемблера. Область ввода/вывода микроконтроллеров AVR Область ввода/вывода микроконтроллеров AVR содержит 64 регистра, ис- пользуемых для управления или хранения данных периферийных устройств. К каждому из этих регистров можно обращаться по адресу ввода/вывода (начиная с 0x000) или по адресу SRAM (в этом случае к адресу ввода/вывода следует при- бавить 0x020). В программах на языке С обычно используются условные имена регистров ввода/вывода, а адреса имеют значение только для программ на языке ассемблера. Имена, адреса ввода/вывода и SRAM, а также краткое описание регистров из области ввода/вывода микроконтроллеров AVR представлены в табл. 1.1. При этом следует отметить, что в различных моделях микроконтроллеров некоторые из перечисленных регистров не используются, а адреса, не указанные в табл. 1.1, зарезервированы компанией Atmel для использования в будущем. Таблица 1.1. Описание регистров из области ввода/вывода микроконтроллеров AVR Имя регистра Адрес ввода/ вывода Адрес SRAM Описание ACSR 0x08 0x28 Регистр управления и состояния аналогового компаратора UBRR 0x09 0x29 Регистр скорости передачи данных через UART UCR ОхОА 0х2А Регистр управления приемопередатчиком UART USR ОхОВ 0x2В Регистр состояния приемопередатчика UART UDR ОхОС 0х2С Регистр данных приемопередатчика UART SPCR OxOD 0x2D Регистр управления интерфейсом SPI SPSR ОхОЕ 0х2Е Регистр состояния интерфейса SPI SPDR OxOF 0x2F Регистр ввода/вывода данных интерфейса SPI PIND 0x10 0x30 Выводы порта D DDRD 0x11 0x31 Регистр направления передачи данных порта D PORTD 0x12 0x32 Регистр данных порта D PINC 0x13 0x33 Выводы порта С DDRC 0x14 0x34 Регистр направления передачи данных порта С PORTC 0x15 0x35 Регистр данных порта С PINB 0x16 0x36 Выводы порта В DDRB 0x17 0x37 Регистр направления передачи данных порта В PORTB 0x18 0x38 Регистр данных порта В PINA 0x19 0x39 Выводы порта А
Память 25 Таблица 1.1. Окончание Имя регистра Адрес ввода/ вывода Адрес SRAM Описание DDRA 0x1 А ОхЗА Регистр направления передачи данных порта А PORTA 0x1 В ОхЗВ Регистр данных порта А EECR 0x1 С ОхЗС Регистр управления памяти EEPROM EEDR 0x1 D 0x30 Регистр данных памяти EEPROM EEARL 0x1 Е ОхЗЕ Регистр адреса памяти EEPROM (младший байт) EEARH 0x1 F 0x3F Регистр адреса памяти EEPROM (старший байт) WDTCR 0x21 0x41 Регистр управления сторожевым таймером ICR1L 0x24 0x44 Регистр захвата таймера/счетчика Т/С1 (младший байт) ICR1H 0x25 0x45 Регистр захвата таймера/счетчика Т/С1 (младший байт) OCR1BL 0x28 0x48 Регистр сравнения В таймера Т/С1 (младший байт) OCR1BH 0x29 0x49 Регистр сравнения В таймера Т/С1 (старший байт) OCR1AL 0х2А 0х4А Регистр сравнения А таймера Т/С1 (младший байт) OCR1AH 0x2 В 0x4В Регистр сравнения А таймера Т/С1 (старший байт) TCNT1L 0х2С 0х4С Счетный регистр таймера/счетчика Т/С1 (младший байт) TCNT1H 0x2 D 0x40 Счетный регистр таймера/счетчика Т/С1 (старший байт) TCCR1B 0x2 Е 0x4 Е Регистр управления В таймера/счетчика Т/С1 TCCR1A 0x2 F 0x4F Регистр управления А таймера/счетчика Т/С1 TCNT0 0x32 0x52 Счетный регистр таймера/счетчика Т/СО ТСС R0 0x33 0x53 Регистр управления таймера/счетчика Т/СО MCUCR 0x35 0x55 Регистр управления микроконтроллером TIFR 0x38 0x58 Регистр флагов прерываний от таймеров/счетчиков TIMSK 0x39 0x59 Регистр маскирования прерываний от таймеров GIFR ОхЗА 0х5А Общий регистр флагов прерываний GIMSK ОхЗВ 0x5В Общий регистр маскирования прерываний SPL 0x3D 0x50 Указатель стека (младший байт) SPH ОхЗЕ 0х5Е Указатель стека (старший байт) SREG 0x3 F 0x5F Регистр состояния Большинство из перечисленных регистров будут рассмотрены позже в ходе изложения материала книги, сейчас же обратим внимание только на регистр со- стояния SREG (аналог в микроконтроллерах PIC — регистр STATUS). Регистр состояния SREG микроконтроллеров AVR Регистр состояния содержит флаги условий микроконтроллеров AVR и рас- полагается в области ввода/вывода по адресу $3F (адрес SRAM — $5F). После по- дачи сигнала сброса он инициализируется нулями. В микроконтроллерах AVR для обозначения результата выполнения операций используются восемь различных флагов: разряд О (С) — флаг переноса (Carry); указывает на переполнение (пере- нос) после выполнения арифметической или логической операции; разряд 1 (Z) — нулевой флаг (Zero); всегда устанавливается, если резуль- тат арифметической или логической операции равен нулю; сбрасывается, если результат операции не равен нулю; разряд 2 (N) — флаг отрицательного результата (Negative); указывает на отрицательный результат после выполнения арифметической или логиче- ской операции;
26 Глава 1. Архитектура микроконтроллеров AVR и PIC разряд 3 (V) — флаг переполнения при вычислениях в дополнительных ко- дах (Two's complement Overflow); поддерживает арифметику дополни- тельных кодов (арифметика кодов с дополнением до двух); устанавливает- ся, если при выполнении соответствующей операции происходит перепол- нение, в противном случае — сбрасывается; разряд 4 (S) — флаг знака (Sign); S = N®V — связь флагов N и V с помощью операции “Исключающее ИЛИ”; флаг знака может применять- ся для определения фактического результата арифметической операции; разряд 5 (Н) — флаг половинного переноса (Half Carry); указывает на пе- реполнение в младшем полубайте (разряды 0...3 байта данных); устанавли- вается, когда происходит перенос из младшего полубайта в старший, в противном случае — сбрасывается; разряд 6 (Т) — флаг копирования (Transfer or Сору); предназначен для сво- бодного применения программистом (например, в качестве буфера); разряд 7 (I) — общее разрешение прерываний (Global Interrupt); если пре- рывания, как таковые, должны быть разрешены, то должен быть установ- лен разряд 7 регистра состояния (в лог. 1). Внутренняя и внешняя память SRAM микроконтроллеров AVR Память SRAM микроконтроллеров AVR предназначена для хранения тех дан- ных, которые не помещаются в рабочих регистрах, а также для организации про- граммного стека (см. следующий подраздел). Данные обычно сохраняют в SRAM, начиная с первых адресов, а стеку соответствуют верхние адреса. Если объема внутренней памяти SRAM недостаточно, то в некоторых микро- контроллерах AVR его можно увеличить до 64 Кбайт посредством подключения внешних блоков памяти. Для этого в регистре MCUCR (адрес в области вво- да/вывода — $35, адрес в SRAM— $55) следует установить в лог. 1 разряд SRE (разряд 7). После установки этого разряда порты А и С будут выступать в качестве шины адреса и шины данных, а выводы 7 и 6 порта D — в качестве управляющих сигналов чтения /RD и, соответственно, записи /WR для внешней памяти SRAM), независимо от того, какие направления передачи данных установлены для этих портов в соответствующих регистрах направления передачи данных. Стек Стек — это особая область памяти данных, используемая процессором для временного хранения адресов возврата из подпрограмм, промежуточных резуль- татов вычислений и др. В микроконтроллерах PIC и некоторых микроконтролле- рах AVR стек реализован аппаратно — для этого выделено отдельное запоми- нающее устройство фиксированного объема в несколько (или несколько десятков) байт. Для микроконтроллеров AVR компиляторы языка С (например, при обра- щении к подпрограммам) могут также создавать один или более стеков программ- но, начиная с верхних адресов области SRAM. Стек действует по принципу LIFO — “Last In, First Out”, что означает “по- следним вошел, первым вышел”. Это означает, что новые данные вначале поме- щаются на вершину (первый уровень) стека, а затем, с поступлением следующих данных, “проталкиваются” на его нижние уровни. Извлечение из стека происхо- дит в обратном порядке: вначале считываются данные, помещенные последними
Память 27 на вершину, после чего данные, размещенные на нижних уровнях, как бы “вытал- киваются” на один уровень вверх. Ячейка памяти, которая является в данный мо- мент вершиной стека, адресуется указателем стека (для AVR — регистровой па- рой SPL, SPH). Поскольку область памяти данных, отводимая для программного стека, огра- ничивается только объемом памяти SRAM, при написании программ следует сле- дить за тем, чтобы стек не становился слишком большим, затирая полезные дан- ные. Память программ Память программ как в микроконтроллерах AVR, так и в микроконтроллерах PIC реализована по технологии Flash-EPROM, которая подразумевает программи- рование пользователем и вытирание электрическим способом. Размер этой памяти варьируется в зависимости от микроконтроллера и обычно составляет несколько Кбайт командных слов. Флэш-память является энергонезависимой, то есть, сохраняет записанную в нее информацию даже после отключения питания микроконтроллера. Несмотря на то, что память этого типа — программируемая, для записи в нее используются только внешние аппаратные средства, поэтому с точки зрения программиста мож- но сказать, что память программ доступна только для чтения. Адресация команд в памяти программ реализуется с помощью специального регистра — счетчика команд, разрядность которого определяет допустимый раз- мер этой памяти. Разрядность ячеек памяти программ, в зависимости от типа мик- роконтроллера, может составлять 14-16 бит. Кроме того, следует отметить, что в микроконтроллерах PIC в первых ячейках памяти программ (начиная с адреса 0x0000) содержатся векторы (адреса перехо- да) сброса и прерываний. Память EEPROMмикроконтроллеров A VR Многие микроконтроллеры AVR оборудованы встроенной памятью EEPROM — электрически перезаписываемой энергонезависимой памятью. Хотя эта память и допускает запись, она редко используется для хранения программных переменных, поскольку, во-первых, медленнодействующая, и, во-вторых, имеет ограниченный (хотя и довольно большой) цикл перезаписи. Учитывая вышесказанное, память EEPROM используют, преимущественно, для хранения данных, которые не должны быть потеряны даже при потере пита- ния. Это очень удобно, к примеру, при калибровке измерительных приборов, ра- ботающих под управлением микроконтроллеров, у которых в памяти EEPROM в процессе настройки сохраняются параметры корректировки. Благодаря этому, в большинстве случаев полностью отпадает необходимость в настроечных потен- циометрах и триммерах. В отличие от флэш-памяти, для записи/чтения памяти EEPROM нет необхо- димости в специальном программаторе — эти операции доступны программно и допускают побайтную передачу данных с помощью регистра управления EECR, регистра данных EEDR и регистровой пары EEARL, EEARH, определяющей адрес ячейки памяти (см. табл. 1.1).
28 Глава 1. Архитектура микроконтроллеров AVR и PIC Запись в память EEPROM Запись байта данных в память EEPROM осуществляется по следующей схеме: 1. Удостовериться, что в разряде EEWE (разряд 1) регистра EECR находится лог. О (разрешение записи). 2. Записать адрес ячейки EEPROM в регистр EEAR. 3. Записать байт данных в регистр EEDR. 4. Установить в лог. 1 разряд EEMWE (разряд 2) регистра EECR. 5. Установить в лог. 1 разряд EEWE (разряд 1) регистра EECR, чтобы активизи- ровать процесс записи. По окончанию цикла программирования разряд EEWE аппаратно автоматиче- ски сбрасывается в лог. 0. Программа пользователя должна непрерывно опраши- вать этот разряд, ожидая появления лог. 0, прежде чем приступить к программи- рованию следующего байта. Чтение из памяти EEPROM Чтение байта данных из памяти EEPROM осуществляется по такой схеме: 1. Записать адрес ячейки EEPROM в регистр EEAR. 2. Установить в лог. 1 разряд EERE (разряд 0) регистра EECR, чтобы активизи- ровать процесс чтения. 3. По окончанию считывания разряда EERE аппаратное обеспечение считывает требуемый байт в регистр EEDR, после чего уже нет необходимости вновь оп- рашивать разряд EERE, поскольку считывание длится только один цикл такта системной синхронизации. Перед началом операции чтения программа пользователя должна постоянно опрашивать разряд EEWE и ждать появления лог. 0. Если во время программиро- вания памяти EEPROM в соответствующий регистр будет записан новый адрес или данные, то еще продолжающийся процесс программирования будет прерван, и результат будет неопределенным! Обработка прерываний Прерывания — это вызовы определенных функций, генерируемые, главным образом, аппаратной частью микроконтроллера. В результате прерывания выпол- нение программы останавливается, и происходит переход к соответствующей подпрограмме обработки прерывания. Прерывания бывают внутренними и внешними. Источниками внутреннего прерывания являются встроенные модули микроконтроллера (например, тай- мер/счетчик или сторожевой таймер). Внешние прерывания вызываются сбросом (сигнал на выводе RESET) или сигналами предустановленного уровня на выводах INT. К примеру, в микроконтроллерах AVR за характер сигналов на выводах INT0/INT1, вызывающих прерывание, определяется с помощью разрядов регистра управления MCUCR: ISC00 (разряд 0), ISC01 (разряд 1) — для входа INTO; ISC 10 (разряд 2), ISC11 (разряд 3) — для входа INT1 (табл. 1.2 и табл. 1.3).
Обработка прерываний 29 Таблица 1.2. Выбор способа активизации прерывания по входу INTO Разряд ISC01 Разряд ISC00 Описание 0 0 Прерывание вызывается по уровню лог. 0 на входе INTO 1 0 Прерывание вызывается по ниспадающему фронту сигнала INTO 1 1 Прерывание вызывается по нарастающему фронту сигнала INTO Таблица 1.3. Выбор способа активизации прерывания по входу INT1 Разряд ISC11 Разряд ISC10 Описание 0 0 Прерывание вызывается по уровню лог. 0 на входе INT1 1 0 Прерывание вызывается по нарастающему фронту сигнала INT1 1 1 Прерывание вызывается по ниспадающему фронту сигнала INT1 В ряде микроконтроллеров PIC выбор фронта для активизации прерывания по входу INT определяется состоянием разряда 6 регистра OPTION: лог. 1 в этом раз- ряде соответствует прерывание по нарастающему, а лог. О — по ниспадающему фронту сигнала. Для установки этого разряда в языке С обычно используют спе- циальные функции. В микроконтроллерах AVR всем прерываниям, включая сброс, поставлен в соответствие собственный вектор прерывания — адрес в начальной области памяти программ, по которому компилятор размещает команду перехода к под- программе обработки прерывания. Перечень векторов прерывания в некоторых моделях микроконтроллеров AVR может выглядеть следующим образом (табл. 1.4). Таблица 1.4. Векторы прерываний в некоторых микроконтроллерах AVR Адрес в памяти программ Источник прерывания Описание 0x0000 RESET Сигнал сброса 0x0001 INTO Внешний запрос на прерывание по входу INTO 0x0002 INT1 Внешний запрос на прерывание по входу INT1 0x0003 Т/С1 Захват по таймеру/счетчику Т/С1 0x0004 Т/С1 Совпадение с регистром сравнения А таймера Т/С1 0x0005 Т/С1 Совпадение с регистром сравнения В таймера Т/С1 0x0006 Т/С1 Переполнение таймера/счетчика Т/С1 0x0007 Т/СО Переполнение таймера/счетчика Т/СО ’ 0x0008 SPI Завершение передачи данных по интерфейсу SPI 0x0009 UART Прием байта приемопередатчиком UART завершен ОхОООА UART Регистр данных приемопередатчика UART пуст 0x000В UART Передача данных приемопередатчиком UART завершена ОхОООС ANA_.COM P Прерывание от аналогового компаратора В микроконтроллерах PIC источники прерывания, кроме RESET, не рассмат- риваются в отдельности, им обычно соответствует один вектор, а в некоторых мо- делях — два вектора для прерываний с различной приоритетностью (в микрокон- троллерах AVR все прерывания имеют одинаковый приоритет, и в случае одно- временного возникновения двух прерываний первым обрабатывается прерывание с меньшим номером вектора). Определять, какое именно прерывание требует об- служивания, — задача программиста, и многие компиляторы с языка С предос-
30 Глава 1. Архитектура микроконтроллеров AVR и PIC тавляют для этой цели готовые функции, освобождающие от необходимости са- мому “вычислять” источник прерывания. В момент возникновения прерывания в стек помещается адрес возврата — адрес команды, которая должна быть выполнена первой после выхода из подпро- граммы обработки прерывания. В результате выполнения последней ассемблер- ной команды подпрограммы обработки прерывания (для микроконтроллеров AVR— это команда reti, а для микроконтроллеров PIC — ret fie) адрес воз- врата извлекается из стека в счетчик команд, и выполнение программы продолжа- ется. Управление прерываниями в микроконтроллерах AVR В микроконтроллерах AVR за управление прерываниями отвечают, главным образом, четыре регистра: GIMSK (General Interrupt Mask Register) — разрешает или запрещает внешние прерывания по входу INT0/1NT1; GIFR (General Interrupt Flag Register) — регистр флагов внешних прерыва- ний; TIMSK (Timer/Counter Interrupt Mask Register) — регистр маскирования прерываний от таймера/счетчика Т/СО и Т/С1; TIFR (Timer/Counter Interrupt Flag Register) — регистр флагов прерываний от таймеров/счетчиков. О состоянии прерывания сигнализирует соответствующий флаг, который ус- танавливается или сбрасывается в регистре флагов. Даже если в регистре маски прерываний установлен соответствующий отдельный разряд разрешения преры- вания, то прерывания могут активизироваться только тогда, когда в регистре со- стояния SREG установлен разряд общего разрешения прерываний I (разряд 7). Ес- ли это имеет место, и наступает прерывание, то выполнение программы ответвля- ется по соответствующему адресу (см. табл. 1.4) и разряд общего разрешения пре- рываний I в регистре SREG сбрасывается в состояние лог. 0, блокируя тем самым последующие прерывания. Если требуется прервать подпрограмму другим преры- ванием, то после входа в подпрограмму обработки прерывания программа пользо- вателя должна установить флаг I в лог. 1. Вместе с входом в подпрограмму обработки прерывания аппаратно сбрасыва- ется также и соответствующий флаг, вызвавший прерывание. Некоторые флаги прерываний могут быть сброшены самим пользователем посредством установки соответствующего флага в лог. 1. Регистр GIMSK Регистр GIMSK (рис. 1.3), расположенный в области ввода/вывода по адресу 0x003В (адрес в SRAM — 0x005В), используется для разрешения внешних пре- рываний. 7 6 5 4 3 2 1 0 INT1 INTO — — — — — — Рис. 1.3. Структура регистра GIMSK микроконтроллеров AVR
Обработка прерываний 31 Если разряд INT1/INT0 установлен в лог. 1, то внешнее прерывание по входу INT1/INT0 будет разрешено до тех пор, пока установлен в лог. 1 разряд I в регистре состояния SREG. Регистр GIFR Состояние внешнего прерывания определяется по регистру GIFR (рис. 1.4), который расположен в области ввода/вывода по адресу ОхООЗА (адрес SRAM — ОхООБА). 7 6 5 4 3 2 1 0 INTF1 INTF0 — — — — — — Рис. 1.4. Структура регистра GIFR микроконтроллеров AVR Флаг INTF1/INTF0 устанавливается в лог. 1, если возникает внешнее преры- вание по сигналу на выводе INT1/INT0. При входе в подпрограмму обработки прерывания этот разряд переводится аппаратно в исходное состояние лог. 0. Регистры TIMSK и TIFR Регистр TIMSK (рис. Е5), расположенный в области ввода/вывода по адресу 0x0039 (адрес в SRAM — 0x0059), используется для разрешения прерываний от таймеров/счетчиков (рассматриваются ниже в этой же главе). 7 6_______5 4_______3_______2_______1_______0 TOIE1 OCIE1A OCIE1B — TICIE1 — TOIEO — Рис. 1.5. Структура регистра TIMSK микроконтроллеров AVR Состояние прерываний, имеющих отношение к таймерам/счетчикам микро- контроллеров AVR, определяется по регистру TIFR (рис. 1.6), который располо- жен в области ввода/вывода по адресу 0x0038 (адрес SRAM — 0x0058). 7 6 5 4 3 2 1 0 TOV1 OCF1A OCF1B — ICF1 — TOV0 — Рис. 1.6. Структура регистра TIFR микроконтроллеров AVR Когда разряд TOIE1 и разряд I в регистре состояния SREG установлены в лог. 1, то разрешено прерывание при переполнении Т/Cl. В случае переполне- ния в регистре TIFR устанавливается флаг TOV1. Если разряд OCIE1A и разряд I в регистре состояния SREG установлены в лог. 1, то разрешено прерывание при совпадении содержимого регистра сравне- ния А с текущим состоянием Т/Cl. В случае совпадения, в регистре TIFR устанав- ливается флаг OCF1 А. Если разряд OCIE1B и разряд I в регистре состояния SREG установлены в лог. 1, то разрешается прерывание при совпадении содержимого регистра срав- нения В с текущим состоянием Т/Cl. В случае совпадения, в регистре TIFR уста- навливается флаг OCF1B. Если разряд TICIE1 и разряд I в регистре состояния SREG установлены в лог. 1, то разрешается прерывание при выполнении условия захвата. Когда воз- никает срабатывание по захвату, в регистре TIFR устанавливается флаг ICF1.
32 Глава 1. Архитектура микроконтроллеров AVR и PIC Если разряд TOIEO и разряд I в регистре состояния SREG установлены в лог. 1, то разрешается прерывание при переполнении таймера/счетчика Т/СО. В таком случае, в регистре TIFR устанавливается флаг TOVO. Установка в лог. 1 одного из флагов в регистре TIFR приводит к переходу по соответствующему вектору прерывания. При входе в подпрограмму обработки прерывания, флаг в регистре TIFR аппаратно сбрасывается в лог. 0. Управление прерываниями в микроконтроллерах PIC В микроконтроллерах PIC управление прерываниями реализовано с помощью регистров специальных функций, и отличается от устройства к устройству. К при- меру, в микроконтроллерах PIC12C6x, PIC14000, Р1С16х для этой цели использу- ются регистры INTCON (рис. 1.7), PIE и PIR, а программы обработки прерываний всегда начинают исполняться с адреса 0x004. 7 6 5 4 3 2 1 0 GIE PEIE TOIE INTE RBIE TOIF INTF RBIF Рис. 1.7. Регистр INTCON микроконтроллеров PIC12C6x, PIC14000, Р1С16х Разряд GIE — это флаг общего разрешения прерываний. Если он установлен в лог. 1, то все немаскированные прерывания разрешены, если же он сброшен в лог. 0, то все прерывания запрещены. Разряд PEIE регистра INTCON может использоваться в качестве флага разре- шения всех прерываний от периферии, определяемых с помощью регистров PIE и PIR. Флаг TOIE разрешает (лог. 1) или запрещает (лог. 0) прерывания при пере- полнении таймера TMR0), а флаг TOIF определяет запрос на соответствующее прерывание. Разряд INTE — флаг разрешения внешнего прерывания по входу INT, а раз- ряд INTF — флаг запроса на прерывания по этому входу. Аналогичное значение, но для порта В имеют разряды RBIE и RBIF. Регистр PIE содержит флаги разрешения прерываний от периферийных уст- ройств, а регистр PIR — соответствующие флаги запросов на прерывание. Пози- ции разрядов в этих регистрах для различных микроконтроллеров отличаются. Микроконтроллеры серии Р1С17Сх В микроконтроллерах серии Р1С17Сх используется четыре вектора прерыва- ний: 0x00008 — внешнее прерывание по входу INT (приоритет — высокий); 0x00010 — прерывание от таймера TMR0 (приоритет — высокий); 0x00018 — внешнее прерывание по входу ТОСК1 (приоритет — высо- кий); 0x00020 — прерывание от периферийных устройств ((приоритет — низ- кий). Для управления прерываниями в этой серии микроконтроллеров используется регистр INTSTА (рис. 1.8).
Обработка прерываний 33 7 6 5 4 3 2 1 О PEIF TOCKIF TOIF INTF PEIE TOCKIE TOIE INTE Рис. 1.8. Регистр INTSTA микроконтроллеров Р1С17Сх Рассмотрим назначение разрядов этого регистра: PEIF — устанавливается в лог. 1 в случае задержки прерывания, иниции- рованного периферийным устройством; TOCKIF — флаг разрешения внешнего прерывания по входу TOCKI (при переходе к вектору 0x00018 аппаратно сбрасывается в лог. 0); TOIF — флаг переполнения счетчика TMR0 (при переходе к вектору 0x00 010 аппаратно сбрасывается в лог. 0); INTF — флаг разрешения внешнего прерывания по входу INT (при пере- ходе к вектору 0x00008 аппаратно сбрасывается в лог. 0); PEIE — флаг разрешения прерываний от периферийных устройств; TOCKIE — флаг разрешения внешних прерываний по входу TOCKI; TOIE — флаг разрешения прерываний от таймера TMR0; INTE — флаг разрешения прерываний по входу INT. Флаги разрешений и запросов на прерывания от внешних устройств находятся в регистрах PIE и PIR. При этом их разряды имеют следующую взаимосвязь с пе- риферией микроконтроллера: 0 (RCIE/RCIF) — прием данных приемопередатчиком UASRT; 1 (TXIE/TXIF) — передача данных приемопередатчиком UASRT; 2 (CA1IE/CA1IF) — модуль ССР1; 3 (CA2IE/CA2IF) — модуль ССР2; 4 (TMR1IE/TMR1 IF) — таймер TMR1; 5 (TMR2IE/TMR2IF) — таймер TMR2; 6 (TMR3IE/TMR3IF) — таймер TMR3; 7 (RBIE/RBIF) — порт В. Микроконтроллеры серии Р1С18Сх В этой серии микроконтроллеров PIC применяется схема управления преры- ваниями, немного напоминающая задействованную в сериях PIC12C6x, PIC14000, Р1С16х. Однако есть четыре важных отличия: к аждому источнику прерываний задается собственный уровень приорите- та; в место одного регистра INTCON используется три (INTCON, INTCON2, INTCON3); вм есто одного регистра PIE и PIR используется по два (PIE1/PIE2; PIR1/PIR2); присутствует регистры 1PR1 и IPR2, задающие приоритеты прерываний от периферийных устройств. 3....6-767
34 Глава 1. Архитектура микроконтроллеров AVR и PIC Рассмотрим вначале регистры INTCON (рис. 1.9). 2 1 0 7 6 5 4 3 INTCON GIE PEIE TMROIE INTOIE RBIE TMROIF INTOIF RBIF 7 6 5 4 3 2 1 0 INTCON2 _RBPU INTEDGo|lNTEDG1 INTEDG2 — TMR0IP — RBIP 7 6 5 4 3 2 1 0 INTCON3 INT2IP INT1IP — INT2IE INT1 IE — INT2IF INT1IF Рис. 1.9. Регистры INTCON микроконтроллеров Р1С18Сх Назначение разрядов регистров INTCON: INTCON: • GIE — флаг общего разрешения прерываний; • PEIE — флаг разрешения всех запросов на прерывание с низким при- оритетом; • TMR0IE — флаг разрешения прерываний от таймера TMR0; • INT0IE — флаг разрешения прерываний по входу INTO; • RBIE — флаг разрешения прерывания по изменению состояния порта В; • TMR0IF — запрос на прерывание от таймера TMR0; • INT0IF — запрос на прерывание по входу INTO; • RBIF — запрос на прерывание по изменению состояния порта В; INTCON2: • INTEDG0 — выбор фронта для внешнего прерывания по входу INTO (лог. 1 — по нарастающему фронту); • INTEDG1 — выбор фронта для внешнего прерывания по входу INT1 (лог. 1 — по нарастающему фронту); • INTEDG2 — выбор фронта для внешнего прерывания по входу INT2 (лог. 1 — по нарастающему фронту); • TMR0IP — задает высокий приоритет для прерывания от таймера TMR0; • RBIP — задает высокий приоритет для прерывания по изменению со- стояния порта В; INTCON3: • INT2IP — задает высокий приоритет для внешних прерываний по входу INT2; • INT1IP — задает высокий приоритет для внешних прерываний по входу INT1; • INT2IE — флаг разрешения внешних прерываний по входу INT2; • INTI IE — флаг разрешения внешних прерываний по входу INT1; • INT2IF — запрос на прерывание по входу INT2; • INT1IF — запрос на прерывание по входу INT1. Структура регистров PIE показана на рис. 1.10.
Сброс 35 7 6 5 4 3 2 1 0 PIE1 PSPIE ADIE RCIE TXIE SSPIE CCP1IE TMR2IE TMR1IE 7 6 5 4 3 2 1 0 PIE2 — — — — BCLIE LVDIE TMR3IE CCP2IE Рис. 1.10. Регистры PIE микроконтроллеров PIC18CX Назначение разрядов регистров PIE: PSPIE — флаг разрешения прерывания от PSP при выполнении операций чтения/записи; ADIE — флаг разрешения прерывания по завершению АЦП; RCIE — флаг разрешения прерывания от USART при приеме данных; TXIE — флаг разрешения прерывания от USART при передаче данных; SSPIE — флаг разрешения прерывания от MSSP; ССР НЕ — флаг разрешения прерывания от модуля ССР1; TMR2IE — флаг разрешения прерывания при совпадении содержимого таймера TMR2 и регистра PR2; TMRITE — флаг разрешения прерывания при переполнении таймера TMR1; BCLIE — флаг разрешения прерывания при конфликте на шине; LVDIE — флаг разрешения прерывания при обнаружении низкого уровня питающего напряжения; TMR3IE — флаг разрешения прерывания при переполнении таймера TMR3; CCP2IE — флаг разрешения прерывания от модуля ССР2. Регистры PIR и IPR имеют сходную структуру и взаимосвязь разрядов с раз- личными устройствами и модулями. Сброс Сброс — это, по сути, одна из форм прерываний, вызывающая перезапуск микроконтроллера (аппаратная инициализация всех регистров управления и пе- риферийных устройств и выполнение программы, начиная с адреса 0x0000). Век- тор сброса всегда расположен самым первым. Возможны следующие варианты сброса: сброс при включении питания; внешний сброс — сигнал сброса подается на соответствующий вывод мик- роконтроллера; сброс от сторожевого таймера — микроконтроллер сбрасывается по исте- чению времени, заданного сторожевым таймером, если этот таймер был разрешен. з*
36 Глава 1. Архитектура микроконтроллеров AVR и PIC В этой книге будет рассмотрен подробнее только третий вид сброса в разделе, посвященном сторожевому таймеру. “Спящие” режимы процессора Микроконтроллеры AVR и PIC допускают переход в “спящий” режим, когда происходит временное отключение генератора тактовых импульсов. В таком ре- жиме потребление энергии сведено к минимуму, а выход из него осуществляется при получении запроса на прерывание. Переход в “спящий” режим реализуется по ассемблерной команде sleep. В случае микроконтроллеров AVR, при этом должен быть предварительно уста- новлен в лог. 1 разряд SE (разряд 5) регистра управления MCUCR. Когда во время режима пониженного энергопотребления происходит преры- вание, центральный процессор выходит из “спящего” режима, выполняет подпро- грамму обработки прерывания и продолжает выполнение программы с команды, следующей после команды sleep. Если во время режима пониженного энергопо- требления поступает сигнал сброса, то центральный процессор выходит из “спя- щего” режима и продолжает выполнение программы с команды, расположенной по адресу $000 в области команд. Для микроконтроллеров AVR может быть выбран один из двух “спящих” ре- жимов: В ждущем режиме (Idle Mode) работа процессора приостанавливается, но таймер/счетчик, сторожевой таймер, система прерываний и тактирования остаются активными. Благодаря этому, центральный процессор может быть возвращен в обычный режим работы с помощью сторожевого тайме- ра, таймера/счетчика или внешнего прерывания. В режиме пониженного энергопотребления (Power Down Mode) систем- ный осциллятор (а значит и весь микроконтроллер) находится в отключен- ном состоянии. В таком режиме с помощью внутреннего RC-генератора колебаний может включаться лишь сторожевой таймер со своим собствен- ным обеспечением тактовой частотой. Активный сторожевой таймер по истечении времени задержки опять переводит микроконтроллер в нор- мальное состояние. Если сторожевой таймер также отключен, то в нор- мальное состояние его может перевести только внешний сигнал сброса или внешнее прерывание. Выбор одного из “спящих” режимов в микроконтроллерах AVR осуществля- ется с помощью разряда SM (разряд 4) регистра управления MCUCR. Если разряд SM установлен в лог. 1, то микроконтроллер переводится в режим пониженного энергопотребления последующей командой sleep, если же разряд SM сброшен в лог. 0, то последующей микроконтроллер переводится в ждущий режим в том случае, если ранее в регистре MCUCR был установлен разряд SE. Таймеры/счетчики Таймеры/счетчики — это, наверное, наиболее часто используемые модули микроконтроллеров. С их помощью можно измерять промежутки времени и час- тоту, определять ширину импульсов, вычислять скорость и т.д. Хотя они и ис-
Таймеры/счетчики 37 пользуются для измерения времени, на самом деле речь идет об обычных двоич- ных счетчиках. В микроконтроллерах AVR и PIC используются как 8-, так и 16-разрядные таймеры/счетчики. Разрядность определяет момент переполнения счетчика (воз- врат в нулевое состояние). Так, для 8-разрядного счетчика переполнение наступа- ет, когда счет достигает 255, а для 16-разрядного — 65535. Количество таймеров/счетчиков и их разрядность в микроконтроллерах отли- чается в зависимости от модели, и потому в данном разделе будут рассмотрены только общие вопросы, имеющие отношение к использованию тайме- ров/счетчиков. Если таймер/счетчик функционирует в качестве счетчика, то он подсчитывает число импульсов, поступающих на определенный вход микроконтроллера. В этом случае соответствующий вывод должен быть сконфигурирован в инициализаци- онной части программы как вход. В случае использования в качестве таймера, частота тактирования тайме- ра/счетчика является производной величиной от такта системной синхронизации внутреннего кварцевого осциллятора. При этом таймеры/счетчики используют в качестве тактового сигнала разделенный такт системной синхронизации. Коэф- фициент деления предварительного делителя частоты может настраиваться инди- видуально для каждого из таймеров с помощью мультиплексора, управляемого разрядами из регистра управления таймера/счетчика. Таймеры/счетчики микроконтроллеров A VR В микроконтроллерах AVR могут использоваться следующие тайме- ры/счетчики: 8- или 16-разрядный Т/СО; 16-разрядный Т/С1; 8- или 16-разрядный Т/С2. Регистры управления в этом случае называются TCCRO, TCCR1 и TCCR2 (расположены в области ввода/вывода), а режим работы и коэффициент деления частоты осциллятора определяется с помощью разрядов CSx2, CSxl и CSxO этих регистров. К примеру, для таймеров/счетчиков Т/СО и Т/Cl выбор режима и вход- ного такта можно определить с помощью комбинаций разрядов, представленных в табл. 1.5. Таблица 1.5. Выбор режима и входного такта для Т/СО и Т/С1 микроконтроллеров AVR CSx2 CSx1 CSxO Описание 0 0 0 Останов 0 0 1 Режим “Таймер”, такт = такт системной синхронизации 0 1 0 Режим “Таймер", такт = такт системной синхронизации / 8 0 1 1 Режим "Таймер”, такт = такт системной синхронизации / 64 1 0 0 Режим “Таймер”, такт = такт системной синхронизации / 256 1 0 1 Режим “Таймер”, такт = такт системной синхронизации /1024 1 1 0 Режим “Счетчик”, такт — внешний на входе ТО (Т1), активный фронт сигнала — ниспадающий 1 1 1 Режим "Счетчик”, такт — внешний на входе ТО (Т1), активный фронт сигнала — нарастающий
38 Глава 1. Архитектура микроконтроллеров AVR и PIC Для Т/С2 комбинации разрядов CS22, CS21 и CS20 могут иметь разное значе- ние для различных моделей микроконтроллеров. т/со Схема работы таймера/счетчика Т/СО, представлена на рис. 1.11. Как только с помощью разрядов CSOO, CS01 и CS02 регистра TCCR0 (адрес 0x33 в области ввода/вывода, адрес 0x53 в SRAM) для делителя частоты будет установлена комбинация, отличная от ООО, таймер/счетчик Т/СО по каждому им- пульсу, поступающему на тактовый вход, начинает увеличивать на единицу со- держимое регистра TCNT0 (адрес 0x32 в области ввода/вывода, адрес 0x52 в SRAM). Когда состояние счетчика в регистре TCNT0 изменяется с OxFF на 0x00, в регистре TIFR (адрес 0x38 в области ввода/вывода) устанавливается флаг переполнения TOV0. ПРИМЕЧАНИЕ Кроме разрядов CSOO, CS01 и CS02 регистра TCCR0 (разряды 0-2), никакие другие разря- ды этого регистра не используются. Таймер/счетчик Т/СО хорошо подходит для оценки временных интервалов. Для этого в ходе выполнения программы в регистр TCNT0 записывается исходное значение. Затем может быть запущен Т/СО с требуемым входным тактом. Про- грамма ожидает появления в регистре TIFR флага переполнения TOV0, указы- вающего на то, что требуемое время истекло. Предположим частота системной синхронизации составляет 4 МГц, а некото- рое действие должно выполняться программой каждые 0,5 с. В этом случае можно воспользоваться делением частоты на 8, что соответствует частоте тактирования 500 кГц или 2 мкс. Таким образом, на подсчет 256 тактовых импульсов счетчику потребуется 512 мкс. Это значение должно быть кратно 500 мкс, чтобы с помо- щью множителя 1000 в программе можно было реализовать требуемое действие в точности с периодом 500 мс. Для этого в счетчик перед началом каждого счета
Таймеры/счетчики 39 должно быть загружено значение 6, чтобы до переполнения выполнялся подсчет не 256, а только 250 тактовых импульсов. Т/С1 16-разрядный таймер/счетчик Т/Cl гораздо сложнее Т/СО (рис. 1.12). Запрос на прерывание при совпадении с В эапрос нэ прерывание при переполнении Т/С1 Запрос на прерывание при захвате входа Запрос на прерывание при совпадении с А Рис. 1.12. Схема таймера/счетчика Т/С1 В дополнение к счетному регистру и регистру управления, он содержит ре- гистр захвата по входу (ICR1) и два 16-разрядных регистра сравнения на выходе (в некоторых моделях микроконтроллеров регистр сравнения В отсутствует).
40 Глава 1. Архитектура микроконтроллеров AVR и PIC Рассмотрим назначение отдельных регистров: TCNT1 — счетный регистр (содержимое счетчика); TCCR1А — регистр управления для определения реакции выводов ОС1А/ОС1В в случае совпадения состояния счетчика в регистре TCNT1 с регистрами сравнения OCR1A/OCR1B, а также для выбора режима ши- ротно-импульсной модуляции; TCCR1B — регистр управления для настройки делителя частоты, для раз- решения подачи сигнала сброса для регистра TCNT1 и для управления за- хватом; ICR1 — регистр захвата по входу (при появлении на выводе ICP фронта входного сигнала, определенного как активный, текущее состояние счет- чика будет перенесено в этот регистр); OCR1 A, OCR1B — регистры сравнения; их содержимое постоянно сравни- вается с состоянием счетчика. В случае совпадения выполняются действия, определенные регистром TCCR1A. Регистр управления TCCR1A (рис. 1.13) находится в области ввода/вывода по адресу 0x2F (адрес 0x4F в SRAM). 7_______6________5 4________3_______2_______1 0 СОМ1А1 СОМ1А0 СОМ1В1 СОМ1В0 — — PWM11 PWM10 Рис. 1.13. Регистр TCCR1А таймера/счетчика Т/С1 Разряды COMI А1/СОМ1 АО и СОМ1В1/СОМ1ВО определяют состояние вы- вода ОС1А/ОС1В при совпадении содержимого регистра сравнения A/В с содер- жимым счетчика. Возможные настройки для режима сравнения показаны в табл. 1.6. Таблица 1.6. Возможные варианты для работы в режиме сравнения СОМ1х1 СОМ1хО Действия в случае совпадения 0 0 Выходное значение отсутствует 0 1 При совпадении ОС1х переключается в другое состояние 1 0 При совпадении на выходе ОС1х устанавливает лог. 0 1 1 При совпадении на выходе ОС1х устанавливает лог. 1 В случае активизации режима ШИМ, разряды 4-7 в регистре TCCR1А имеют значения, отличные от указанных в табл. 1.6. Когда регистр управления TCCR1A определяет работу в конфигурации широтно.-импульсного модулятора, то Т/С1 работает как суммирующий и вычитающий счетчик, осуществляя циклические перех'оды от 0x0000 к максимальному значению ТОР, и затем снова возвращаясь к 0x0000. При запрограммированной разрешающей способности ШИМ в N раз- рядов значение ТОР рассчитывается как TOP = 2N- 1. Частота ГШим, с которой повторяются циклы ШИМ, вычисляется по формуле: fpwM = fr/ci / (2N+1 - 2), причем частота таймера/счетчика fT/Ci выбирается с помощью разрядов CSIO- CS 12 регистра TCCR1B, а разрешающая способность N — с помощью разрядов
Таймеры/счетчики 41 PWM10 и PWM11 регистра TCCR1A. Соответствующие взаимосвязи показаны в табл. 1.7. Таблица 1.7. Выбор режима ШИМ с помощью разрядов PWM11 и PWM10 PWM11 PWM10 Разрешающая способность Значение ТОР Частота ШИМ 0 0 Режим ШИМ не активен 0 1 8 разрядов OxOOFF (255) fT/ci / 510 1 0 9 разрядов 0x01 FF (511) fT/ci /Ю22 1 1 10 разрядов 0x03FF (1023) ff/d / 2046 Когда состояние счетчика в регистре TCNT1 совпадает со значением 10 млад- ших разрядов регистра OCR1A/OCR1B, то, в зависимости от состояния разрядов СОМ1А1/СОМ1АО или СОМ1В1/СОМ1ВО регистра TCCR1A, вывод ОС1А/ОС1В последующим тактовым импульсом устанавливается или сбрасывается. Соответ- ствующие взаимосвязи показаны в табл. 1.8. Таблица 1.8. Возможности выбора для режима сравнения при работе ШИМ СОМ1х1 COMIxO Действие в случае совпадения 0 0 На выводе ОС1х нет никакого сигнала 0 1 На выводе ОС1х нет никакого сигнала 1 0 Неинвертирующий широтно-импульсный модулятор. В случае соответствия, при суммирующем подсчете на выводе ОС1х устанавливается лог. 0, а при подсчете с вычитанием — лог. 1 1 1 Инвертирующий широтно-импульсный модулятор. В случае соответствия, при суммирующем подсчете на выводе 001 х устанавливается лог. 1, а при подсчете с вычитанием —лог. 0 В случае неинвертирующего широтно-импульсного модулятора, коэффициент заполнения g прямоугольного сигнала на выводе с ШИМ соответствует значению n / (2N - 1), где п — значение в соответствующем регистре OCR, a N — разре- шающая способность ШИМ в разрядах (рис. 1.14). и ин и AV Ul Период Т =1н+ К Коэффициент заполнения д = tn / Т Среднее Оду = (Он Ан + LEAl)/ Т Рис. 1.14. Определение периода Т, коэффициента заполнения д и среднего арифметического Uav пря- моугольных импульсов напряжения U Если регистр сравнения OCR1A/OCR1B содержит значение ТОР или 0, то на соответствующем выводе, в соответствии с правилами, представленными в табл. 1.9, постоянно поддерживаются уровень лог. 0 или лог. 1. На рис. 1.15. на примере фиктивной трехразрядной ШИМ показано формиро- вание неинвертированного и инвертированного выходного ШИМ-сигнала на вы- ходе ОС1В. На диаграмме А показан примерный вид ступенчатого сигнала, соот- ветствующий состоянию счетчика TCNT1, на диаграмме В — неинвертирован- ный, а на диаграмме С — инвертированный выходной сигнал. Продолжительность
42 Глава 1. Архитектура микроконтроллеров AVR и PIC периода TPWM в этом случае вычисляется в соответствии с рассмотренным выше уравнением TPWM = TT/ci ’ (2N+I - 2). Таким образом, при N = 3 период ШИМ- сигнала состоит из 14 периодов тактового сигнала fr/ci на входе TCNT1. Таблица 1.9. Вывод ШИМ для особых случаев OCR1x = ТОР или OCR1x = О СОМ1х1 СОМ1хО OCR1X Вывод ОС1х 1 0 0 0 1 0 ТОР 1 1 1 0 1 1 1 ТОР 0 Состояние ШММ-вывод °С,В СОМ1В0 = 0 Высокий 1 уровень В Низкий _______ уровень 0123456789 10 15 НН 1М-вывод : : 0С1В ,сом1во = 1 i i Высокий 1 ; t * fy/CI уровень с Низкий_____ уровень 01 23456789 10 15 20 25 tх ?Т/С1 Рис. 1.15. Способ формирования неинвертированных и инвертированных выходных ШИМ-сигналов В данном примере регистр сравнения OCR1B содержит значение 5. В регист- ре TCNT1, учитывая тот факт, что его исходное значение равно 0, значение 5 по- является после пяти тактовых импульсов. На следующем тактовом импульсе, по- сле распознания совпадения на выводе ОС1В устанавливается уровень лог. 0 (рис. 1.15, В). Регистр TCNT1 инкрементируется далее до тех пор, пока не будет достигнуто значение ТОР, которое при трехразрядной ШИМ составляет 7. Как только достиг- нуто значение ТОР, направление счета меняется на обратное, и регистр выполняет вычитание. После девятого тактового импульса, начиная от стартового значения 0, содержимое регистра TCNT1 опять совпадает с содержимым регистра OCR1B. На следующем тактовом импульсе на выходе ОС1В устанавливается уровень лог. 1.
Таймеры/счетчики 43 Регистр TCNT1 декрементируется далее до тех пор, пока опять не будет дос- тигнуто значение 0. Это происходит после в общей сложности четырнадцати так- товых импульсов, считая от начального значения 0. Таким образом завершается период ШИМ-сигнала, направление счета вновь меняется на обратное и регистр TCNT1 опять выполняет сложение. Как видно на рис. 1.15 (В), “высокая” состав- ляющая выходного сигнала составляет 6 тактовых периодов, а “низкая” — 4. Та- ким образом, коэффициент заполнения g = 6/10 или g = 3/5. Аналогично, диаграм- ма С на рис. 1.15 показывает соотношения для инвертированного выходного ШИМ-сигнала. В режиме ШИМ устанавливается флаг переполнения TOV1, если счетчик при достижении состояния 0 меняет направление счета на обратное. Это прерывание по Т/Cl при переполнении, как и при нормальной работе в режиме счетчика, вы- зывается в том случае, если установлен флаг общего разрешения прерываний I в регистре состояния SREG, а также флаг TOIE1 в регистре TIMSK. В соответст- вии с этим, прерывания при совпадении регистров TCNT1 и OCR1A/OCR1B вы- зываются тогда, когда в регистре TIMSK установлен флаг общего разрешения прерываний и флаг OCIE1A/OCIE1B. В отношении таймера/счетчика Т/Cl осталось рассмотреть еще регистр управ- ления TCCR1B (адресу 0х2Е в области ввода/вывода, адрес 0х4Е в SRAM). Струк- тура регистра TCCR1B показана на рис. 1.16. 7 6 5 4 3 2 1 0 ICNC1 ICES1 — — СТС1 CS12 CS11 CS10 Рис. 1.16. Регистр TCCR1B таймера/счетчика Т/С1 Как уже было сказано ранее, разряды 0-2 используются для выбора частоты тактирования Т/Cl (см. табл. 1.5). Если разряд СТС1 установлен в лог. 1, то Т/С1 возвращается в состояние 0x0000 по импульсу такта системной синхронизации, следующего после совпадения содержимого счетчика и регистра сравнения А. При работе в режиме ШИМ этот разряд на процесс работы никак не влияет. Разряд ICES1 определяет, каким образом должна осуществляться передача состояния счетчика в регистр захвата ICR1: по нарастающему (ICES 1 = 1) или по ниспадающему фронту (ICES 1=0). Разряд ICNC1 определяет, должно ли быть активизировано подавление помех (если ICNC1 = 0, то подавление помех отключено). Для подавления кратковремен- ных импульсов помех, которые могут привести к ошибочному запуску, входной сигнал зондируется на протяжении четырех периодов такта системной синхрониза- ции. Только после того как будут распознаны четыре последовательных низких или высоких уровня входного сигнала, что определяется разрядом ICES 1, при активном подавлении помех будет выполнена запись текущего состояния счетчика в регистр ICR1. Т/С2 Таймер/счетчик Т/С2 обычно имеет разрядность 8 бит и реализует функции сравнения на выходе и ШИМ, аналогичные Т/Cl. Основная особенность Т/С2 за- ключается в том, что в качестве источника тактовых импульсов он может исполь- зовать генератор, независимый от системного. Для управления Т/С2 используются два регистра: ASSR (рис. 1.17) и TCCR2 (рис. 1.18).
44 Глава 1. Архитектура микроконтроллеров AVR и PIC 7 6 5 4 3 2 1 О — — — — AS2 TCN2UB OCR2UB TCR2UB Рис. 1.17. Регистр ASSR таймера/счетчика Т/С2 Если установить в лог. 1 разряд AS2, то в качестве источника тактовых им- пульсов можно использовать внешний осциллятор. Оставшиеся три разряда (0-2) используются в программах для проверки того, что данные не записываются в ре- гистры Т/С2 в тот момент, когда они обновляются аппаратно. Такая проверка не- обходима по той причине, что осциллятор Т/С2 работает асинхронно по отноше- нию к системному осциллятору. 7 6 5 4 3 2_______1________0 PWM2 СОМ21 СОМ20 СТС2 CS22 CS21 CS20 Рис. 1.18. Регистр TCCR2 таймера/счетчика Т/С2 Установка в лог. I разряда PWM2 переводит Т/С2 в режим ШИМ. Назначение разрядов СОМ21 и СОМ20 идентично назначению разрядов СОМ 1x1 и СОМ 1x0 таймера/счетчика Т/Cl -— выбор режима сравнения на выходе. Разряд СТС2 определяет, должен ли счетчик сбрасываться в нуль при совпа- дении его содержимого с регистром сравнения. Разряды 0-2 определяют частоту тактового сигнала, полученного с помощью предварительного делителя частоты такта системной синхронизации. Таймеры/счетчики микроконтроллеров PIC Описанное выше применение таймеров/счетчиков микроконтроллеров AVR справедливо также и для таймеров микроконтроллеров PIC. Здесь используются аналогичные принципы измерения ширины и частоты импульсов, а также широт- но-импульсной модуляции, режимов сравнения и захвата. В микроконтроллерах PIC могут использоваться три таймера: TMRO, TMR1 и TMR2. TMR0 TMR0 (рис. 1.19) — это 8-разрядный таймер/счетчик. Таким образом, счет для него ограничен диапазоном 0—255. Его тактирование реализуется от внешнего ис- точника или на основании такта системной синхронизации. Рис. 1.19. Схема таймера/счетчика TMR0
Таймеры/счетчики 45 Для управления работой таймера TMR0 используются следующие разряды регистра OPTION REG: разряд 5 — TOCS — определяет выбор источника синхроимпульсов (0 — внутренний: 1 — внешний на входе TOCKI); разряд 4 — T0SE — определяет выбор фронта, по которому происходит увеличение содержимого счетного регистра TMR0 (0 — по нарастающему; 1 — по ниспадающему фронту тактового сигнала); разряд 3 — PSA — использование предварительного делителя частоты (О — делитель используется для управления таймером TMR0; 1 — для управления сторожевым таймером); разряды 0-2 — PSO, PSI, PS2 — выбор коэффициента деления частоты входного тактового сигнала (табл. 1.10). Таблица 1.10. Назначение разрядов PSO - PS2 регистра OPTION_REG PS2 PS1 PS0 Коэффициент деления частоты входного тактового сигнала 0 0 0 2 0 0 1 4 0 1 0 8 0 1 1 16 1 0 0 32 1 0 1 64 1 1 0 128 1 1 1 256 Для управления прерываниями от таймера TMR0 используются следующие разряды регистра INTCON: разряд 2 — T0IF — флаг прерывания при переполнении TMR0; разряд 5 — TOIE — флаг разрешения прерывания при переполнении TMR0; разряд 7 — GIE — флаг общего разрешения прерываний. TMR1 TMR1 (рис. 1.20) — это 16-разрядный таймер/счетчик, который может ис- пользоваться для формирования запросов на прерывание, подобно TMR0, или же работать в режимах захвата, сравнения и ШИМ. Тактирование таймера TMR1 осуществляется от сигнала системной синхро- низации или от специального генератора, предназначенного для работы с относи- тельно медленными программными приложениями. Как правило, используется кварцевый резонатор частотой 32,768 кГц. Для управления таймером TMR1 используется регистр T1CON (рис. 1.21). На- значение отдельных разрядов регистра T1CON: TMR1ON — подключение таймера (0 — отключен, 1 — включен); TMR1CS — выбор источника тактирующих сигналов (0 — такт системной синхронизации; 1 — генератор 32,768 кГц);
46 Г лава 1. Архитектура микроконтроллеров AVR и PIC Т1 SYNC — включение/отключение синхронизация специального генера- тора с генератором импульсов системной синхронизации (0 — включена; 1 — отключена); T1OSCEN — разрешение/запрет тактирования таймера TMR1 от специаль- ного генератора (0 — генератор отключен; 1 — тактирование разрешено); T1CKPS0, T1CKPS1 — выбор коэффициента деления частоты (табл. 1.11). Рис. 1.20. Схема таймера/счетчика TMR1 7 6 5________4________3________2_________1 0 — — T1CKPS1 T1CKPS0 T10SCEN T1SYNC TMR1CS TMR1ON Рис. 1.21. Регистр T1CON микроконтроллеров PIC Таблица 1.11. Назначение разрядов T1CKPS0 - T1CKPS1 регистра T1CON T1CKPS1 T1CKPS0 Коэффициент деления частоты тактового сигнала 0 0 1 0 1 2 1 0 4 1 1 8 Счетный регистр таймера TMR1 представляет собой регистровую пару TMR1H, TMR1L, а управление прерываниями осуществляется с помощью разря- дов регистров PIR1 иР1Е1: регистр PIR1: • разряд 0 — TMR1IF — флаг переполнения TMR1; • разряд 2 — ССР 1 IF — флаг прерывания при возникновении захвата по входу; регистр PIE1: • разряд 0 — TMR1IE — флаг разрешения прерывания при переполнении TMR1; • разряд 2 — ССР НЕ — флаг разрешения прерывания при возникновении захвата по" входу.
Таймеры/счетчики 47 TMR2 Назначение таймера TMR2 (рис. 1.22) — измерение временных интервалов для реализации ШИМ, обеспечения определенной скорости обмена по последова- тельному порту и т.п. В этом смысле он подобен таймеру TMR0. Таймер TMR2 тактируется импульсами, следующими с частотой такта сис- темной синхронизации, деленной на четыре. Каждый раз, когда содержимое счет- ного регистра TMR2 совпадает с содержимым регистра PR2, таймер автоматиче- ски сбрасывается в исходное (нулевое) состояние. Рис. 1.22. Схема таймера/счетчика TMR2 При каждом совпадении TMR2 и PR2 генерируется запрос на прерывание, частоту возникновения которого можно также масштабировать с помощью вы- ходного делителя частоты. Для управления таймером TMR2 используется регистр T2CON (рис. 1.23). 7 6 5 4 3 2 1 О — TOUTPS3 TOUTPS2 TOUTPS1 TOUTPSO TMR2ON T2CKPS1 T2CKPS0 Рис. 1.23. Регистр T2CON микроконтроллеров PIC Назначение отдельных разрядов регистра T2CON: T2CKPS0-T2CKPS1 —управление предварительным делителем частоты (табл. 1.12); TMR2ON — подключение таймера (0 — отключен, 1 — включен); TOUTPSO-TOUTPS3 — выбор коэффициента деления частоты запросов на прерывание при TMR2=PR2 (табл. 1.13). Таблица 1.12. Назначение разрядов T2CKPS0 - T2CKPS1 регистра T2CON T1CKPS1 T1CKPS0 Коэффициент деления частоты тактового сигнала 0 0 1 0 1 4 1 X 16
48 Глава 1. Архитектура микроконтроллеров AVR и PIC Таблица 1.13. Назначение разрядов TOUTPSO - TOUTPS3 регистра T2CON TOUTPS3 TOUTPS2 TOUTPS1 TOUTPSO Коэффициент деления частоты запросов 0 0 0 0 1 0 0 0 1 2 0 0 1 0 3 0 0 1 1 4 0 1 0 0 5 0 1 0 1 6 0 1 1 0 7 0 1 1 1 8 1 0 0 0 9 1 0 0 1 10 1 0 1 0 11 1 0 1 1 12 1 1 0 0 13 1 1 0 1 14 1 1 1 0 15 1 1 1 1 16 Для организации прерываний используются разряды TMR2IE (флаг разреше- ния) и TMR2IF (флаг прерывания) регистров PIE1 и PIR1 соответственно. Для того чтобы эффективно использовать таймер TMR2, используются сле- дующие формулы: Т = (4 К1 К2 • PR2) / F; PR2 = Т F / (4 • К1 • К2), где Т — требуемая временная задержка, К1 — коэффициент деления предвари- тельного делителя частоты; К2 — коэффициент деления делителя частоты запро- сов на прерывание; PR2 — содержимое регистра PR2; F — частота системной синхронизации. Модуль ССР Таймеры TMR1 и TMR2 микроконтроллеров PIC применяются в составе мо- дуля сравнения/захвата/ШИМ -— ССР (Compare-Capture-PWM). Таких модулей может быть два: ССР1 и ССР2, — управление которыми реализовано с помощью регистров CCPxCON (рис. 1.24). 7 6 5 4 3 2 1 О — — DC1BX1 DC1ВХО ССР1МЗ ССР1М2 ССР1М1 ССР1М0 Рис. 1.24. Регистр CCPxCON микроконтроллеров PIC Назначение отдельных разрядов регистра CCPxCON: ССР 1 МО - ССР 1 М3 — выбор режима захвата/сравнения (табл. 1.14); DC 1ВХО - DC 1ВХ1 — два младших разряда 10-разрядной ШИМ.
Сторожевой таймер 49 Таблица 1.14. Назначение разрядов ССР1М1 -ССР1МЗ регистра CCPxCON ССР1МЗ ССР1М2 ССР1М1 ССР1М0 Значение 0 0 X X Модуль ССР отключен 0 1 0 0 Захват по каждому ниспадающему фронту 0 1 0 1 Захват по каждому нарастающему фронту 0 1 1 0 Захват по каждому 4-му нарастающему фронту 0 1 1 1 Захват по каждому 16-му нарастающему фронту 1 0 0 0 В случае совпадения на выходе — высокий уро- вень 1 0 0 1 В случае совпадения на выходе — низкий уро- вень 1 0 1 0 В случае совпадения — запрос на прерывание 1 0 1 1 Особый случай режима сравнения 1 1 X X Режим ШИМ В режиме захвата (то есть, фиксации значения таймера в момент появления определенного условия) используются регистры CCPR1H, CCPR1L (в случае TMRI) или CCPR2H, CCPRL (в случае TMR2). В таком режиме таймер выполняет функции счетчика тактовых импульсов, и при наступлении условия захвата его содержимое переписывается в регистровую пару CCPRx. В режиме сравнения модуль ССР формирует сигнал на выходе ССРх в том случае, когда содержимое счетного регистра становится равным значению, запи- санному в регистровой паре CCPRxL, CCPRxH. Этот режим обычно используется для выдачи сигналов на внешние устройства по истечении некоторого временного интервала. В режиме ШИМ таймер работает как делитель частоты, формирующий пери- од ШИМ-сигнала. Его значение постоянно сравнивается с содержимым регистра PR2, и при совпадении компаратор сбрасывает таймер в исходное состояние, по- сле чего цикл повторяется. Параллельно организован контур сравнения, вклю- чающий в себя таймер, второй компаратор и регистр CCPRxH. Выходы обоих компараторов управляют RS-триггером, выход которого соединен с выводом ССРх. Вначале триггер устанавливается в “1” по сигналу сброса таймера, а по сигна- лу компаратора контура сравнения — сбрасывается в “0”. Таким образом, на вы- ходе RS-триггера формируется сигнал с периодом, определяемым содержимым регистров PR2 и CCPRxH. Сторожевой таймер Сторожевой таймер (watchdog timer) — встроенный таймер, тактируемый внутренним RC-осциллятором, который автоматически сбрасывает микрокон- троллер при переполнении своего счетного регистра. В частности, он использует- ся для предотвращения перехода микроконтроллера в режим бесконечного цикла, когда на него невозможно повлиять извне. Обобщенная структурная схема сторо- жевого таймера показана на рис. 1.25. В микроконтроллерах AVR и PIC управление сторожевым таймером несколь- ко отличается. Так, в микроконтроллерах AVR для этого используется регистр управления WDTCR (адрес в области ввода/вывода — 0x21, адрес SRAM — 0x41) (рис. 1.26). 4-6-767
50 Глава 1. Архитектура микроконтроллеров AVR и PIC Рис. 1.25. Структурная схема сторожевого таймера 7 6 5 4________3________2_________1________0 — — — WDTOE WDE WDP2 WDP1 WDP0 Рис. 1.26. Регистр WDTCR микроконтроллеров AVR Назначение отдельных разрядов регистра WDTCR: WDP0-WDP2 — выбор коэффициента деления частоты следования сигна- лов сброса (при этом период до наступления сброса зависит от рабочего напряжения процессора — табл. 1.15); WDE — включение/отключение сторожевого таймера (1 — включен); WDTOE — если сторожевой таймер должен быть отключен, следует уста- новить этот разряд в лог. 1. После установки этого разряда он в течение че- тырех периодов такта системной синхронизации остается в состоянии лог. 1, а затем аппаратно сбрасывается в лог. 0. Программа пользователя имеет возможность отключить сторожевой таймер посредством записи лог. 0 в разряд WDE только во время этих четырех тактов системной синхрони- зации. Таблица 1.15. Назначение разрядов WDP0 - WDP2 регистра WDTCR WDP2 WDP1 WDP0 Коэффициент деления Период до сброса (при Vcc = 5 В) Период до сброса (при Vcc = 3 В) 0 0 0 1 16 мс 47 мс 0 0 1 2 32 мс 94 мс 0 1 0 4 64 мс 190 мс 0 1 1 8 128 мс 380 мс 1 0 0 16 256 мс 750 мс 1 0 1 32 512 мс 1,5с 1 1 0 64 1 с 3 с 1 1 1 128 2,1 с 6 с В системе команд AVR сторожевой таймер сбрасывается в исходное состоя- ние по команде wdr. В микроконтроллерах PIC для управления сторожевым таймером предназна- чен рассмотренный выше регистр OPTION. Для этого разряд PSA должен быть установлен в лог. 1, чтобы предварительный делитель частоты был переключен на использование совместно со сторожевым таймером, а не с TMR0. Коэффициент деления выбирается с помощью разрядов PS2-PS0 (табл. 1.16). В отличие от микроконтроллеров AVR, в микроконтроллерах PIC отсутствует возможность включать/отключать сторожевой таймер с помощью регистра управ-
Параллельные порты ввода/вывода 51 ления. Единственный способ предотвратить сброс от сторожевого таймера— пе- риодически выполнять ассемблерную команду clrwdt. Таблица 1.16. Выбор коэффициента деления частоты следования сигналов сброса от сторожевого таймера в микроконтроллерах PIC PS2 PS1 PS0 Коэффициент деления Период до сброса 0 0 0 1 18 мс 0 0 1 2 36 мс 0 1 0 4 72 мс 0 1 1 8 144 мс 1 0 0 16 288 мс 1 0 1 32 576 мс 1 1 0 64 1,2 с 1 1 1 128 2,3 с Параллельные порты ввода/вывода Параллельные порты — это особые устройства ввода/вывода, позволяющие передавать во внешний мир или принимать одновременно восемь разрядов дан- ных. Для обозначения портов используются латинские буквы А, В, С и т.д. Коли- чество портов ввода/вывода варьируется в зависимости от модели микроконтрол- лера. В микроконтроллерах AVR каждому параллельному порту ввода/вывода по- ставлены в соответствие три регистра (букве х соответствует имя порта А, В и т.д.): DDRx — регистр направления передачи данных — определяет, является тот или иной вывод порта входам или выходом; если некоторый разряд ре- гистра DDRx содержит лог. О, то соответствующий вывод порта сконфигу- рирован как вход, в противном случае — как выход; PORTx — регистр порта — если вывод выполняет роль выхода, то в соот- ветствующий разряд записывается значение, предназначенное для вывода; если вывод выполняет роль входа, то лог. О в некотором разряде регистра PORTx соответствует высокоомный вход, а лог. 1 — вход, нагруженный подтягивающим сопротивлением; PINx — регистр выводов порта — в отличие от регистров DDRx и PORTx доступен только для чтения и позволяет считать входные данные порта на внутреннюю шину микроконтроллера. Выводы портов зачастую выполняют различные альтернативные функции при работе со внутренними и периферийными модулями микроконтроллеров AVR. Так, к примеру, в некоторых моделях в качестве внешних тактовых входов тайме- ров/счетчиков Т/СО и Т/С 1 используются разряды 0 и 1 порта В или 4 и 5 порта D. Точное назначение выводов портов следует сверять по спецификации микрокон- троллера. В микроконтроллерах PIC каждому параллельному порту ввода/вывода по- ставлены в соответствие два регистра: | PORTx — регистр данных порта; 4*
52 Глава 1. Архитектура микроконтроллеров AVR и PIC TRISx — регистр направления передачи данных через выводы порта (лог. 1 в некотором разряде этого регистра соответствует режим ввода, а лог. О — режим вывода). Режим PSP порта D микроконтроллеров PIC В микроконтроллерах PIC серии 18Сх порт D может работать в режиме управ- ляемого параллельного порта PSP (Parallel Slave Port). Это означает, что он дейст- вует как регистр, который может быть подключен к шине другого микроконтрол- лера, обмениваясь с ним данными. В режиме PSP, как и в случае обмена данными с любым периферийным устройством, используются сигналы RD (чтение), WR (запись) и CS (выбор кристалла) — разряды 0-2 порта Е (пример — рис. 1.27). Микроконтроллер Внешний в режиме PSP микроконтроллер Рис. 1.27. Пример подключения внешнего микроконтроллера PIC в режиме PSP Для управления режимом PSP используется регистр TRISE (рис. 1.28). 7 6 5 4 3 2 1 0 IBF OBF IBOV PSPMODE — TRISE2 TRISE1 TRISE0 Рис. 1.28. Регистр TRISE микроконтроллеров PIC Режим PSP активизируется путем установки в лог. I разряда PSPMODE. Пре- рывания разрешаются установкой в лог. 1 разряда PSPIE (разряд 7) регистра PIE1, а запросы формируются в разряде PSPIF (разряд 7) регистра PTR1. С помощью разрядов 0-2 регистра TRISE осуществляется выбор режима для соответствующих разрядов порта Е. Когда на линиях CS и RD (выводы RE2 и RE0) одновременно появляется низ- кий уровень сигнала, содержимое регистра OUTREG выводится через порт D. При записи в регистр OUTREG устанавливается в лог. 1 разряд OBF регистра TRISE — это означает, что выходной буфер заполнен данными. После передачи данных разряд OBF автоматически сбрасывается в лог. 0. Когда на линиях CS и WR (выводы RE2 и RE1) одновременно появляется низ- кий уровень сигнала, осуществляется прием данных через порт D. Принятая вели- чина сохраняется в регистре INREG, при этом автоматически устанавливается в лог. 1 разряд IBF регистра TRISE. После программного считывания содержимо- го регистра INREG этот разряд автоматически сбрасывается в лог. 0. Если ранее принятый байт не считывается до поступления следующего байта в регистр INREG, устанавливается в лог. 1 разряд IBOV регистра TRISE, указы- вающий на переполнение входного буфера.
Последовательный ввод/вывод 53 Последовательный ввод/вывод В отличие от параллельного обмена данными, в случае последовательного ввода/вывода используется только одна информационная линия. При этом переда- ча данных бывает асинхронной и синхронной. При синхронном последовательном вводе/выводе синхронизируется пере- дача отдельных битов данных с помощью одновременно передаваемого тактового сигнала. Синхронная последовательная передача данных применяется, главным образом, на уровне печатных плат, в том числе — для обмена данными между различными интегрированными блоками в составе схемы микроконтроллера и различными периферийными схемами (например, для обработки видеосигнала). В противоположность этому, при асинхронной передаче данных передается не тактовый сигнал, а старт-бит и стоп-бит, определяющие начало и завершение передачи слова данных (рис. 1.29). Рис. 1.29. Типичный формат асинхронной передачи данных (в данном примере — байта 10000010) Главной областью применения асинхронной передачи данных, как правило, является не обмен данными в составе схемы, а коммуникация между блоками, разделенными пространственно и обладающими признаками собственного интел- лекта. В качестве примера можно назвать связь между персональным компьюте- ром и принтером, модемом, программирующим устройством или регистратором данных. В микроконтроллерах AVR асинхронная передача данных осуществляется с помощью приемопередатчика UART, а в микроконтроллерах PIC — приемопе- редатчика USART или по шине CAN. Для синхронного ввода/вывода использует- ся особый режим приемопередатчика USART, а также интерфейсы SPI и 12С (в микроконтроллерах PIC — с помощью порта MSSP). Рассмотрим перечислен- ные средства подробнее. Приемопередатчик UARTмикроконтроллеров A VR Для работы UART выделены в общей сложности четыре регистра: регистр управления UCR (адрес в области ввода/вывода — 0х0А, адрес SRAM — 0х2А) — предназначен для управления функциями приемопере- датчика и для разрешения/запрета прерываний от UART (рис. 1.30); регистр состояния USR (адрес в области ввода/вывода— 0x0В, адрес SRAM — 0x2В) (рис. 1.31);
54 Глава 1. Архитектура микроконтроллеров AVR и PIC регистр данных UDR (адрес в области ввода/вывода — ОхОС, адрес SRAM — 0x2с) — физически состоит из двух регистров, обращение к ко- торым осуществляется по одному и тому же адресу; один из них использу- ется для передачи, а другой — для приема данных; регистр UBRR (адрес в области ввода/вывода — 0x0 9, адрес SRAM — 0x2 9) — применяется для настройки требуемой скорости передачи данных с помощью встроенного контроллера, позволяющего устанавливать наибо- лее распространенные скорости передачи по стандарту RS232C. 7 6_______5_______4_______3_______2_______1________0 RXCIE TXCIE UDRIE RXEN TXEN CHR9 RXB8 ТХВ8 Рис. 1.30. Регистр управления UCR микроконтроллеров AVR Если разряд RXCIE и разряд общего разрешения прерываний I в регистре со- стояния SREG установлены в лог. 1, то разрешается прерывание по завершению приема через UART. Если разряд TXCIE и разряд общего разрешения прерываний I в регистре со- стояния SREG установлены в лог. 1, то разрешается прерывание по завершению передачи через UART. Если разряд UDRIE и разряд общего разрешения прерываний I в регистре со- стояния SREG установлены в лог. 1, то разрешается прерывание по опустошению регистра данных UART. Если разряд RXEN установлен в лог. 1, то происходит разблокирование при- емника, и вывод 0 порта D становится входом UART. Если разряд RXEN содер- жит лог. 0, то принимающий элемент приемопередатчика UART блокируется, и вывод 0 порта D может использоваться в качестве обычного входа/выхода. Если разряд RXEN содержит лог. 0, то флаги OR и FE регистра состояния USR не могут быть установлены. Если эти флаги все же установлены, то они с помощью RXEN не сбрасываются. Если разряд TXEN установлен в лог. 1, то происходит разблокирование пере- датчика, а вывод 1 порта D становится выходом UART. Если разряд TXEN содер- жит лог. 0, то передающий элемент UART блокируется, и вывод 1 порта D может использоваться в качестве обычного входа/выхода. Если разряд TXEN во время процесса передачи устанавливается в лог. 0, то передатчик не блокируется до тех пор, пока текущий символ в сдвиговом регистре, а также символ, возможно, ожи- дающий на передачу в регистре UDR не будут полностью переданы. Если разряд CHR9 установлен в лог. 1, то слова данных, подлежащие пере- даче/считыванию имеют длину 11 бит (9 разрядов данных плюс стартовый и стоп- бит). Девятый бит из разряда ТХВ8 при передаче попадает в UCR, а при прие- ме — в разряд RXB8 регистра UCR. Девятый бит может быть использован для размещения дополнительных информационных данных, например, в качестве би- та четности или второго стоп-бита. Если разряд CHR9 установлен в лог. 0, то сло- ва данных, подлежащие передаче/считыванию имеют длину 10 бит (8 разрядов данных плюс стартовый и стоп-бит). 7 6 5 4 3 2 1 0 RXC ТХС UDRE FE OR — — — Рис. 1.31. Регистр состояния USR микроконтроллеров AVR
Последовательный ввод/вывод 55 Регистр состояния USR информирует программу пользователя о состоянии приемопередатчика UART. Флаг RXC устанавливается в лог. 1, если принятое слово данных было пере- несено из сдвигового регистра в регистр UDR (без учета возможных ошибок кад- рирования, которые могли возникнуть во время передачи данных). После чтения регистра UDR флаг RXC автоматически сбрасывается в лог. 0. Флаг ТХС будет установлен в лог. 1, если символ в сдвиговом регистре был передан полностью (то есть, включая стоп-бит), и из регистра UDR не ожидается новый байт данных. Флаг очень полезен в полудуплексном режиме работы, когда непосредственно после передачи необходимо переключиться в режим приема. При входе в подпрограмму обработки прерывания флаг завершения передачи ТХС аппаратно сбрасывается в лог. 0. Флаг UDRE устанавливается в лог. 1, если содержимое регистра UDR было перенесено в сдвиговой регистр. С его помощью пользователь получает уведом- ление о том, что приемопередатчик готов к передаче нового байта. Соответст- вующая подпрограмма обработки прерывания выполняется до тех пор, пока уста- новлен флаг UDRE. Флаг UDRE сбрасывается при записи байта данных в регистр UDR. В случае сброса при включении питания флаг UDRE устанавливается в лог. 1, чтобы показать, что приемопередатчик готов к передаче нового байта данных. Флаг FE устанавливается в лог. 1 при обнаружении ошибки кадрирования. Это происходит, если при трех сканированиях стоп-бита был более одного раза обнаружен лог. 0, и тем самым стоп-бит был распознан как сигнал низкого уровня. Флаг FE сбрасывается, когда стоп-биту соответствует сигнал высокого уровня. Пользовательская программа должна постоянно проверять флаг FE перед чтением регистра UDR, чтобы можно было распознать потенциально некорректный символ в регистре приема. Флаг OR устанавливается в лог. 1, если один из символов, переданных в ре- гистр UDR из сдвигового регистра, не был прочитан перед следующим поступив- шим символом. Этот флаг обновляется после считывания действительного симво- ла из регистра UDR, поэтому пользовательская программа должна всегда прове- рять флаг OR после чтения регистра UDR, чтобы распознать потерю одного по- ступившего символа. Флаг OR сбрасывается при переносе считанного символа в регистр UDR. Настройка скорости передачи данных через UART В приемопередатчик UART встроен специальный контроллер скорости пере- дачи данных, представляющий собой делитель частоты для определения скорости передачи данных на основании такта системной синхронизации. Скорость передачи может быть вычислена по следующему уравнению: fBaud = O/(16(UBRR+l)) где fBaud — скорость передачи в бодах, Ф — такт системной синхронизации; UBRR — содержимое 8-разрядного регистра UBRR (0...255). Значения, записываемые в регистр UBRR для наиболее распространенных скоростей передачи данных, представлены в табл. 1.17. Все значения, для которых погрешность получается меньше 2%, в табл. 1.17 выделены полужирным шриф- том.
56 Глава 1. Архитектура микроконтроллеров AVR и PIC Таблица 1.17. Значения регистра UBRR для наиболее распространенных скоростей передачи данных и частоты работы кварцевого осциллятора Скорость передачи данных, бод 1,8432 МГц Погреш- ность (%) 3,6864 МГц Погреш- ность (%) 4 МГц Погреш ность (%) 1200 UBRR = 95 0,0 UBRR = 191 0,0 UBRR = 207 0,2 2400 UBRR = 47 0,0 UBRR = 95 0,0 UBRR = 103 0,2 4800 UBRR = 23 0,0 UBRR = 47 0,0 UBRR = 51 0,2 9600 UBRR = 11 0,0 UBRR = 23 0,0 UBRR = 25 0,2 14400 UBRR=7 0,0 UBRR = 15 0,0 UBRR = 16 2,1 19200 UBRR = 5 0,0 UBRR = 11 0,0 UBRR = 12 0,2 2400 UBRR = 207 0,2 UBRR = 287 — UBRR = 312 — 4800 UBRR = 103 0,2 UBRR = 143 0,0 UBRR = 155 0,2 9600 UBRR = 51 0,2 UBRR = 71 0,0 UBRR = 77 0,2 14400 UBRR = 34 0,8 UBRR = 47 0,0 UBRR = 51 0,2 19200 UBRR = 25 0,2 UBRR = 35 0,0 UBRR = 38 0,2 Значения, выделенные курсивом, превышают 255, и потому не могут быть ус- тановлены в регистре UBRR, имеющем длину всего 8 разрядов. Если потребуется соответствующая скорость передачи данных, то необходимо переходить на более низкие частоты колебаний кварцевого осциллятора. Приемопередатчик USARTмикроконтроллеров PIC В микроконтроллерах PIC скорость обмена данными через приемопередатчик USART задается восьмиразрядным счетчиком во взаимодействии с регистром SPBRG. Когда содержимое счетчика и регистра SPBRG совпадает, счетчик сбра- сывается в нуль. На значение скорости передачи через USART также влияет со- стояние разряда BRGH регистра управления TXSTA (см. рис. 1.32). Этот разряд определяет, какая требуется скорость для передачи данных: высокая (BRGH = 1) или низкая (BRGH = 0). Значение для записи в регистр SPBRG вычисляется по следующей формуле: SPBRG = (Fosc ’ 4brgh) / (64 • скорость передачи) - 1. Например, для того чтобы получить скорость передачи 9600 бод при микро- контроллере с рабочей частотой FOsc = Ю МГц в регистр SPBRG должно быть за- писано значение (10-106 • 4) / (64 • 9600) - 1 = 64,1 (округляем до 64). Управление приемопередатчиком USART осуществляется с помощью регист- ров TXSTA, который используется для управления процессом передачи (рис. 1.32), и RCSTA, который используется для управления процессом приема (рис. 1.33). 7 6 5 4 3 2 1 0 CSRC TX9 TXEN SYNC — BRGH TRMT TX9D Рис. 1.32. Регистр TXSTA микроконтроллеров PIC В отличие от приемопередатчика UART микроконтроллеров AVR, устройство USART может обмениваться данными не только в асинхронном, но и в синхрон- ном режиме. В этом случае поразрядный сдвиг данных из сдвигового регистра в линию передачи (или наоборот) осуществляется по синхроимпульсам самого
Последовательный ввод/вывод 57 приемопередатчика USART или внешнего устройства. Выбор режима работы USART осуществляется с помощью разряда SYNC регистра TXSTA (1 — син- хронный режим; 0 — асинхронный режим), а выбор источника тактовых импуль- сов в синхронном режиме — с помощью разряда CSRC (1 — внутренний источ- ник; 0 — внешний источник). Назначение остальных разрядов регистра TXSTA: TX9D — девятый бит передаваемых данных, если ТХ9 = 1; TRMT — флаг, сигнализирующий о завершении передачи байта; TXEN — флаг разрешения передачи данных; ТХ9 — разрешение (лог. 1) передачи данных в девятиразрядном формате. 7 6 5 4 3 2 1 О SREN RX9 SREN CREN ADDEN FERR OERR RX9D Рис. 1.33. Регистр RCSTA микроконтроллеров PIC Назначение разрядов регистра RCSTA: RX9D — девятый бит принимаемых данных, если RX9 = 1; OERR — флаг, указывающий на переполнение буфера приема; FERR — флаг, указывающий на обнаружение ошибки в формате прини- маемых данных; ADDEN — разрешение обнаружения адреса в асинхронном режиме пере- дачи девяти бит данных— в некоторых микроконтроллерах PIC приемник USART может использоваться для приема сразу двух байтов в формате “данные:адрес”, где адрес предназначен для идентификации устройства, связанного с шиной данных (в таком случае подпрограмма обработки пре- рывания вначале проверит адрес запрошенного устройства и только потом обработает байт данных); CREN — разрешение режима непрерывного приема; SREN — флаг разрешения однократного приема в синхронном режиме (по- сле приема данных автоматически сбрасывается в лог. 0); RX9 — флаг разрешения приема девяти бит данных; SREN — флаг активизации USART (для передачи битов данных использу- ется вывод 6 порта С, а для приема — вывод 7 того же порта). Флаги запросов (TXIF и RCIF) и разрешения (TXIE и RCIE) прерываний от USART находятся в регистрах PIR и PIE. Синхронная передача данных по интерфейсу SPI Интерфейс SPI (Serial Peripheral Interface) служит для обмена данными с пе- риферийными устройствами. В качестве таких устройств могут выступать про- стые сдвиговые регистры или буквенно-цифровые модули индикации, а также сложные микропроцессорные системы или системы регистрации данных. Многие компании-изготовители предлагают большой выбор устройств с интерфейсом SPI.
58 Глава 1. Архитектура микроконтроллеров AVR и PIC В случае обмена данными по интерфейсу SPI микроконтроллер работает в режиме ведущего устройства (Master), взаимодействуя с одним или несколькими ведомыми блоками (Slave). Схема передачи данных по интерфейсу SPI микрокон- троллеров AVR/PIC показана на рис. 1.34 (MSB — старший разряд, a LSB — младший разряд передаваемого байта). Рис. 1.34. Схема передачи данных по интерфейсу SPI микроконтроллеров AVR/PIC Ведущее устройство берет на себя активную часть обмена данными, вызывая передачу и управляя процессом. Ведомое устройство не может само быть актив- ным. Оно принимает и передает данные только тогда, когда происходит его акти- визация со стороны ведущего устройства по линии /SS. Ведущее устройство также генерирует такт для передачи по выходной линии SCK. Для ведомого блока вывод SCK является входом, через который он получает от Master-устройства такти- рующие сигналы. Если ведомое устройство активизируется ведущим по линии /SS, то начинается обмен данными: Master записывает подлежащий передаче байт в свой сдвиговой ре- гистр данных (для микроконтроллеров AVR — регистр SPDR, для микроконтрол- леров Р1С — регистр SSPBUF). С помощью каждого выработанного тактового им- пульса Master перемещает один бит данных на выход MOSI/SDO, a Slave одновре- менно в обратном направлении передает один бит на вход MISO/SDI ведущего бло- ка. Таким образом, в течение цикла SPI, состоящего из восьми тактовых импульсов, Master и Slave обмениваются байтом данных. По окончании передачи данных в регистре состояния интерфейса SP1 уста- навливается флаг соответствующего запроса на прерывание. Этот флаг указывает на окончание передачи и вызывает запрос на прерывание после того как в регист- ре управления SPI будет установлен разряд разрешения на прерывание от интер- фейса SPI. В режиме “Master” текущая передача данных может быть преждевре- менно завершена выдачей в линию /SS сигнала лог. 1. К интерфейсу SPI ведущего устройства можно подключать несколько ведо- мых устройств (пример для микроконтроллеров AVR — на рис. 1.35), однако ак- тивным будет только то из них, на вход /SS которого будет подан уровень лог. 0. Выходы MISO незадействованных ведомых блоков находятся в высокоомном со- стоянии и не влияют на процесс передачи данных. В примере на рис. 1.35 устройство G2, с точки зрения ведущего устройства, является только блоком передачи (например, ЦАП с интерфейсом SPI), а устрой- ство G3 — только блоком приема.
Последовательный ввод/вывод 59 Рис. 1.35. Подключение нескольких ведомых устройств к одному ведущему по SPI (для AVR) В микроконтроллерах AVR в качестве линий SPI используются выводы порта В (см. табл. 1.18). В микроконтроллерах PIC для последовательной синхронной передачи данных используется порт MSSP, работающий в режиме интерфейса SPI или 12С (этот интерфейс рассматривается в следующем подразделе). Линиям SP1 соответствуют выводы порта А и С (табл. 1.18). Таблица 1.18. Распределение выводов портов для линий интерфейса SPI Линия Микроконтроллеры AVR Микроконтроллеры PIC /SS Разряд 4 порта В Зависит от модели (например, разряд 5 порта А) MOSI (SDO) Разряд 5 порта В Разряд 5 порта С MISO(SDI) Разряд 6 порта В Разряд 4 порта С SCK Разряд 7 порта В Разряд 3 порта С Регистры управления и состояния SPI в микроконтроллерах AVR Регистр управления SPCR интерфейса SPI в микроконтроллерах AVR (рис. 1.36) находится в области ввода/вывода по адресу OxOD (адресу 0x2D в SRAM). 7 6 5 4'3 2 1 О SPIE SPE DORD MSTR CPOL СРНА SPR1 SPR0 Рис. 1.36. Регистр управления SPCR микроконтроллеров AVR По окончании передачи данных через интерфейс SPI аппаратная часть уста- навливает в регистре состояния SPCR разряд SPIF (флаг прерываний от интерфей- са SPI). Этот флаг указывает на завершение передачи, и приводит к запросу на прерывание как только в регистре управления SPCR будет установлен разряд SPIE, а в регистре состояния SREG — флаг общего разрешения прерываний I.
60 Глава 1. Архитектура микроконтроллеров AVR и PIC Разряд SPE активизирует интерфейс SPI (лог. 1) или отключает его (лог. 0). После поступления сигнала сброса этот разряд принимает значение лог. 0, и тем самым система SPI отключается. Если разряд DORD содержит лог. 0, то сначала будет передан старший разряд байта данных. При DORD = 1 первым передается младший разряд. Когда разряд MSTR содержит лог. 0, то система SPI определяется как ведо- мая (Slave), а при MSTR = 1 она будет определена как ведущая (Master). Когда ли- ния /SS в режиме Master сконфигурирована как вход, то разряд MSTR при низком уровне сигнала на выводе /SS сбрасывается в лог. 0, и тем самым интерфейс SPI определяется как Slave. В этом случае в регистре состояния устанавливается флаг SPJF. Когда разряд CPOL содержит лог. 0, то на выходе SCK в неактивном состоя- нии находится сигнал низкого уровня. Если CPOL = 1, то на SCK в неактивном состоянии находится сигнал высокого уровня. С помощью этого разряда, а также разряда СРНА (выбор фазы синхронизации) устанавливается один из четырех ре- жимов передачи данных по интерфейсу SPI (табл. 1.19). Разряды SPR1 и SPR0 при работе интерфейса SPI в режиме Master служат для выбора тактовой частоты для линии SCK. Если система SPI сконфигурирована как Slave, то эти разряды не имеют никакого значения. Взаимосвязь между разрядами SPR1, SPR0 и частотой импульсов в линии SCK показана в табл. 1.20. Таблица 1.19. Режимы передачи данных по интерфейсу SPI в микроконтроллерах AVR CPOL СРНА Описание режима 0 0 Master переводит линию /SS в состояние лог. 0. Для соответствующего ведомого блока передача начинается по ниспадающему фронту этого сигнала. Его выход MISO переходит из высокоомного в активное со- стояние, и старший разряд байта, находящегося в его регистре данных SPDR, появляется на выходе MISO. Собственно передачу данных Master начинает записью подлежащего передаче байта данных в свой регистр SPDR. Вслед за этим на выходе MOSI ведущего блока появляется старший разряд. На протяжении первой половины первого тактового импульса тактовая линия еще ос- тается в состоянии покоя для того, чтобы обеспечить стабильную уста- новку на соответствующем входе бита данных. По нарастающему фронту первого и каждого последующего тактового импульса принимаются биты, расположенные на входах Master и Slave, а по ниспадающему фронту следующий бит сдвигается дальше. После восьмого тактового импульса передача данных завершена, фла- ги SPIF в регистрах состояния ведущего и ведомого блоков установле- ны, а содержимое сдвиговых регистров будут перенесено в соответст- вующие приемные буферы. Выход MOSI ведущего блока возвращается в состояние покоя (лог. 1), а на выходе MISO ведомого блока, как пра- вило, находится старший разряд байта, только что принятого ведущим блоком. Одновременно со сбросом линии /SS в исходное состояние (лог. 1) Master завершает передачу, Slave становится неактивным, а его выход MISO переходит в высокоомное состояние 0 1 Аналогично описанному выше случаю с той разницей, что состояние покоя тактовой линии здесь устанавливается при лог. 1, биты данных принимаются по первому и каждому последующему тактовому импуль- су, а сдвиг осуществляется по нарастающему фронту сигнала
Последовательный ввод/вывод 61 Таблица 1.19. Окончание CPOL СРНА Описание режима 1 0 Для того чтобы при этом режиме начать передачу данных, Master, как и в первом случае, переводит линию /SS в состояние лог. 0. Блок Slave разблокирован, и его выход MISO переходит из высокоомного в актив- ное состояние. Логический уровень на MISO для этого случая не опре- делен, но, как правило, на MISO находится младший разряд байта, пе- реданного во время предыдущей передачи от Slave к Master. Собственно передачу данных Master в этом режиме начинает посред- ством записи байта данных, подлежащего передаче, в регистр SPDR. Для ведомого блока передача начинается по нарастающему фронту тактового сигнала. Старшие разряды подлежащих передаче байтов в ведущем и ведомом блоках с помощью нарастающего фронта перво- го тактового импульса устанавливаются на выходе MOSI ведущего блока (выходе MISO ведомого блока). По ниспадающему фронту пер- вого и каждого последующего тактового импульса они переносятся на входы Master и Slave, а по нарастающему фронту следующий разряд сдвигается. После восьмого тактового импульса передача данных завершается, ус- танавливаются флаги SPIF в регистрах состояния интерфейсов Master и Slave, а содержимое их сдвиговых регистров переносится в соответ- ствующие буферы приема. Выход MOSI ведущего блока возвращается в состояние покоя (лог. 1), на выходе MISO ведомого блока остается младший разряд байта, только что переданного ведущему блоку. Од- новременно с возвратом в исходное состояние линии ZSS (лог. 1) Master завершает передачу в целом, Slave становится неактивным, а его выход MISO переходит в высокоомное состояние. 1 1 Аналогично описанному выше случаю стой разницей, что состоянием покоя тактовой линии здесь является лог. 1, а биты данных сдвигаются по ниспадающему фронту первого и каждого последующего тактового импульса, а принимаются по нарастающему фронту Таблица 1.20. Частота импульсов в линии SCK в зависимости от разрядов SPR1, SPR0 SPR1 SPR0 Частота импульсов в линии SCK 0 0 Частота системной синхронизации / 4 0 1 Частота системной синхронизации /16 1 0 Частота системной синхронизации / 64 1 1 Частота системной синхронизации /128 Регистр состояния SPSR интерфейса SP1 в микроконтроллерах AVR располо- жен в области ввода/вывода по адресу ОхОЕ (0x2Е в SRAM). В этом регистре ис- пользуются только разряды 6 и 7: Разряд 6 — флаг WCOL — устанавливается в том случае, когда во время передачи данных через интерфейс SPI предпринимается попытка записи в регистр данных SPI, что приводит к разрушению только что переданного байта данных. По этой причине текущая передача данных доводится до за- вершения, а новый байт не записывается в сдвиговый регистр интерфейса SPI. Флаг WCOL должен быть сброшен пользователем вручную посредст- вом считывания регистра состояния и последующего обращения к регистру данных интерфейса SPI. Разряд 7 — флаг SPIF — указывает на завершение передачи и вызывает за- прос на прерывание, как только в регистре управления SPCR будет уста-
62 Глава 1. Архитектура микроконтроллеров AVR и PIC новлен разряд SPIE, а в регистре состояния SREG — разряд I. Когда линия /SS в режиме Master сконфигурирована как вход, то при низком уровне сигнала на выводе линии ZSS также будет установлен флаг SPIF. Флаг SPIF сбрасывается автоматически аппаратной частью при выполнении подпро- граммы обработки прерывания от интерфейса SPI. Альтернативно, сброс может быть выполнен вручную посредством считывания регистра состоя- ния SPSR и последующего обращения к регистру данных интерфейса SPI. Регистры управления и состояния SPI в микроконтроллерах PIC Для управления интерфейсом SPI в микроконтроллерах используются регист- ры SSPSTAT (рис. 1.37) и SSPCON1 (рис. 1.38). 7 6 5 4 3 2 1 О SMP СКЕ D/A Р S R/W UA BF Рис. 1.37. Регистр SSPSTAT микроконтроллеров PIC Назначение разрядов регистра SSPSTAT: BF — флаг заполнения буфера данных; UA, R/W, S, Р, D/A — имеют отношение к рассмотренному ниже последо- вательному интерфейсу 1~С; СКЕ — выбор активного фронта импульсов SCK для передачи данных (ис- пользуется совместно с разрядом СКР регистра SSPCON1, аналог в микро- контроллерах AVR — разряд SPHA регистра SPCR); SMP — точка стробирования данных в режиме Master (1 — стробирование в конце битового интервала; 0 — стробирование в середине интервала). В режиме Slave этот разряд всегда содержит лог. 0. 7 6 5 4 3 2 1 0 WCOL SSPOV SSPEN СКР SSPM3 SSPM2 SSPM1 SSPM0 Рис. 1.38. Регистр SSPCON1 микроконтроллеров PIC Назначение разрядов регистра SSPCON1: SSPM0-SSPM3 — выбор режима работы порта MSSP (значения для ин- терфейса SPI представлены в табл. 1.21); СКР — выбор фронта для передачи (аналог в микроконтроллерах AVR — разряд CPOL регистра SPCR); если СКР=0, то передача осуществляется по нарастающему фронту тактового сигнала; SSPEN — флаг разрешения работы интерфейса SPI; SSPOV — флаг переполнения приемного буфера; WCOL — флаг коллизий при записи (1 — запись новых данных в буфер- ный регистр была произведена в момент передачи).
Последовательный ввод/вывод 63 Таблица 1.21. Выбор режима работы порта MSSP для интерфейса SPI SSPM3 SSPM2 SSPM1 SSPM0 0 0 0 0 Режим Master, частота = Fosc / 4 0 0 0 1 Режим Master, частота = Fosc /16 0 0 1 0 Режим Master, частота = Fosc / 64 0 0 1 1 Режим Master, частота = выход TMR2 / 2 0 1 0 0 Режим Slave, вывод SS разрешен 0 1 0 1 Режим Slave, вывод SS не используется 2 Синхронная передача данных по интерфейсу I С Интерфейс I2C (Inter-integrated Circuit), состоящий только из двух линий (SDA и SCL), предназначен для низкоскоростного последовательного обмена данными с периферийными устройствами, подсоединенных к одной общей шине (рис. 1.39). SDA (линия последовательной передачи данных) (линия последовательной передачи та кто в ьв импульсов.) g I : I I | I | jj I j I I j । ij I . |j I Рис. 1.39. Подключение устройств к шине 12С Характеристики шины 12С: работа только с двумя линиями, благодаря чему требуется меньше мест со- единения и минимизируются затраты на проводку; зона действия — до 3 м; возможность работать в режиме с одним ведущим блоком (Single-Master) или с несколькими ведущими блоками (Multi-Master). Линия SCL используется для передачи синхроимпульсов. Если данные не пе- редаются, она содержит высокий уровень сигнала. Линия SDA используется для побитной передачи данных. Если данные не передаются, то она имеет высокий потенциал. Передаваемые данные действительны в фазе высокого уровня такта и могут менять свое состояние только в фазе низкого уровня. Каждый блок, подсоединенный к шине, во время передачи данных может быть или приемником или передатчиком, а также ведущим (Master) или ведомым (Slave). Ведущее устройство инициирует передачу данных. В частности, Master занимает шину тем, что генерирует стартовый сигнал на линии SCL и начинает обмен с ведомым устройством. До тех пор, пока на шине работает ведущее уст- ройство, не может быть активным никакой другой Master-блок. ПРИМЕЧАНИЕ‘ Тактовый сигнал на линии SCL может быть сформирован только ведущим устройством.
64 Глава 1. Архитектура микроконтроллеров AVR и PIC Ведомым является устройство, по адресу которого обращается ведущее уст- ройство с требованием передачи данных. Теоретически Master может одновре- менно снабжать одними и теми же данными несколько ведомых устройств. В роли ведущего устройства в большинстве случаев выступает микропроцессор, обору- дованный или специальной аппаратной частью с шиной типа ГС, или контролле- ром шины, работающим под управлением специального программного обеспече- ния. Протокол шины 12С Для того чтобы несколько блоков могли обмениваться данными, необходим некоторый протокол, который описывает процесс передачи данных по шине и в любой момент времени не допускает ошибочной интерпретации состояния ши- ны. В случае шины с несколькими ведущими устройствами (что относится и к 12С) необходимо также установить, когда и какое ведущее устройство имеет право за- нимать шину. Эти условия регулируются протоколом шины (рис. 1.40). S: Условие начала D: Бит данных (не может быть изменен, когда SCL = выс ур.) Р: Условие завершения А: Подтверждение (от приемника о получении байта) MSB: Старший значащий бит LSB: Младший значащий бит Рис. 1.40. Протокол передачи байта данных через шину 12С Работа шины и, следовательно, ее занятость, определяется условием начала передачи — изменение ведущим устройством состояние линии SDA с высокого уровня на низкий. После наступления условия начала передачи шина будет занята ведущим устройством, создавшим это условие, вследствие чего все другие веду- щие устройства будут заблокированы. Условие начала является однозначным со- стоянием на шине, потому что смена уровня сигнала на линии SDA, как правило, допускается только тогда, когда тактовая линия SCL находится в состоянии низ- кого уровня. Все устройства, подключенные к шине, должны распознать условие начала передачи и переключаются на прием (ведомое устройство/приемник). Условие на- чала передачи также учитывается и другим ведущим устройством, которое со сво- ей стороны имело намерение занять шину. В результате оно немедленно отзывает свое требование. Устройство, создавшее условие начала передачи, в данный мо- мент времени является для шины ведущим (Master), а все остальные устройства — потенциальными ведомыми (Slave). Ведущее устройство теперь отвечает за такто- вый сигнал и становится передатчиком. После создания условия начала ведущее устройство начинает передачу дан- ных. Оно переводит тактовую шину в состояние низкого уровня и теперь может занять линию передачи данных затребованным информационным разрядом (высо-
Последовательный ввод/вывод 65 кий или низкий уровень в линии SDA). Затем тактовая шина опять переводится в состояние высокого уровня. ПРИМЕЧАНИЕ Изменение в линии SDA может происходить только в фазе низкого уровня сигнала в линии SCL. Во время фазы высокого уровня в линии SCL линия SDA должна быть стабильной. Передача данных, как правило, выполняется побайтно, при этом первым пе- редается старший разряд. После передачи полного байта данных, состоящей из восьми тактовых циклов, следует бит подтверждения от приемника. Бит подтверждения (квитирования) — это реакция приемника на принятый байт данных. Он является для передатчика признаком того, что приемник физиче- ски присутствует и “прослушивает” линию. Одновременно с этим бит подтвер- ждения можно рассматривать как сигнал синхронизации. Бит подтверждения, как правило, генерируется приемником. Если ведущее устройство принимает от ведомого устройства несколько байтов данных, то оно квитирует каждый отдельный байт битом подтверждения, за исключением по- следнего. Такое отрицательное квитирование сообщает ведомому устройству, что передача данных завершена, и далее последует условие завершения или новое ус- ловие начала передачи. Для передачи бита подтверждения ведущее устройство генерирует на линии SCL дополнительный тактовый импульс (рис. 1.41). Приемник выдает сигнал по- ложительного квитирования, переводя линию SDA в состояние низкого уровня, или отрицательного квитирования, переводя линию SDA в состояние высокого уровня. SCL (от ведущего устройства) Шина занята SD/A - выход ведущего устройства SDA - выход ведомого устройства S: Условие начала Р: Условие завершения О: Бит данных (не может быть изменен, когда SCL = выс. ур.) А: Подтверждение (от приемника о получении байта) MSB: Старший значащий бит LSB: Младший значащий бит Рис. 1.41. Выработка бита квитирования приемником, работающим в режиме ведомого устройства Таким образом, для передачи одного байта данных, как правило, требуется девять тактовых циклов. После передачи одного байта данных и приема бита под- 5 — 6-767
66 Глава 1. Архитектура микроконтроллеров AVR и PIC тверждения передача данных может быть сразу же продолжена. Если приемник реагирует на передачу байта данных отрицательным квитированием, то ведущее устройство должно завершить передачу данных, опять освободив шину. Шина после окончания передачи данных, которая может состоять из любого количества байтов, опять освобождается ведущим устройством. Освобождение шины осуществляется с помощью условия завершения — изменение в линии SDA уровня сигнала с низкого на высокий в то время, когда по тактовой шине SCL пе- редается сигнал высокого уровня. После создания условия завершения передачи шина освобождается. Условие завершения передачи также представляет собой однозначное состояние на шине. Все блоки и устройства распознают его и подготавливаются к появлению нового условия начала передачи. В том случае, если ведущее устройство из-за промежу- точной занятости шины отзывает свое требование занять шину, то оно может предпринять новую попытку создать условие начала передачи и тем самым полу- чить шину для своих нужд. ПРИМЕЧАНИЕ Полная передача данных через шину 12С, в принципе, состоит из условия начала передачи, одного или нескольких байтов данных (за которыми, соответственно, следует бит квитиро- вания), и условия завершения передачи. Адресация ведомых устройств Выбор ведомого устройства, с которым хотело бы обмениваться данными ве- дущее устройство, осуществляется посредством первого байта, который всегда определяется как адрес ведомого устройства — первый байт последовательности данных. Он однозначно сопоставлен определенному устройству, подключенному к шине, и имеет длину 7 бит (разряды от 1 до 7). Теоретически, таким образом можно адресовать до 128 ведомых устройств, однако по определению некоторые адреса ведомых устройств имеют особое значение. Адрес ведомого устройства со- стоит из двух частей: постоянной и переменной (рис. 1.42). Постоянная часть адреса ведомого устройства Переменная часть адреса ведомого устройства Разряд — направления передачи данных Бит7 БитВ Битб Бмт4 БитЗ БитЗ Бит! БитО R /W Рис. 1.42. Формат адреса шины 12С Постоянная часть адреса описывает требования к определенным группам уст- ройств и определяется изготовителем. Его длина определена в результате практи- ческого опыта и в большинстве случаев составляет 4 бита. Он будет тем короче, чем больше однотипных устройств в схеме. Постоянная часть адреса жестко “прошита” в интегральной схеме и не может быть изменена пользователем. Переменная часть адреса ведомого устройства служит для выбора определен- ного устройства из группы однотипных кристаллов, среди которых все имеют по- стоянную часть адреса ведомого устройства. Благодаря этому, к шине могут быть подсоединены несколько однотипных интегральных схем. Переменная часть
Последовательный ввод/вывод 67 в большинстве случаев определяется пользователем с помощью внешних схем (через дополнительные выводы). С помощью разрядов 1-7 адреса ведомого устройства однозначно идентифи- цируется требуемый ведомый блок. Разряд 0 задает направление передачи дан- ных. Он определяет, должны ли быть приняты или переданы данные. Если разряд направления передачи данных содержит лог. 1 (чтение), то ведущее устройство находится в режиме приемника, а ведомое — в режиме передатчика. Если разряд направления передачи данных содержит лог. О (запись), то ведущее устройство будет работать как передатчик, а ведомое — как приемник. Адрес ведомого устройства также подтверждается этим устройством с помощью бита квитирования. Если ведущее устройство после адресации полу- чает отрицательное квитирование, то оно может заключить, что ведомое устрой- ство или вообще отсутствует, или в настоящий момент с ним невозможно устано- вить связь (например, оно занято обработкой заданий, критическими с точки зре- ния времени). Работа с интерфейсом 12С в микроконтроллерах PIC Интерфейс 12С аппаратно реализован не во всех микроконтроллерах AVR, од- нако, благодаря высокому допустимому значению тактовой частоты, возможна его организация с помощью программного обеспечения. В микроконтроллерах PIC обмену данными по интерфейсу 12С соответствует особый режим работы порта MSSP. Линии SCL соответствует вывод 3 порта С, а линии SDA — вывод 4 того же порта. Для управления передачей в режиме 12С используются три регистра: уже рас- смотренные выше SSPSTAT (см. рис. 1.38) и SSPCON1 (см. рис. 1.39), а также SSPCON2 (рис. 1.43). Назначение разрядов регистра SSPSTAT, имеющих отношение к 12С: BF — флаг заполнения буфера данных; UA — устанавливается в лог. 1 в том случае, если необходимо модифици- ровать адрес устройства (содержимое регистра SSPADD); R/W — указывает на тип операции: 0 — запись, 1 — чтение; S — устанавливается в лог. 1 при обнаружении условия начала передачи; Р — устанавливается в лог. 1 при обнаружении условия завершения пере- дачи; D/A — признак переданного байта: 0 — байт адреса, 1 — байт данных. Назначение разрядов регистра SSPCON1, имеющих отношение к 12С: SSPM0-SSPM3 — выбор режима работы порта MSSP (значения для ин- терфейса 12С представлены в табл. 1.22); СКР — установка этого разряда в лог. 1 разрешает тактирование; SSPEN — флаг разрешения работы интерфейса; SSPOV — флаг переполнения приемного буфера; WCOL — флаг коллизий при записи (1 — запись новых данных в буфер- ный регистр была произведена в момент передачи). 5*
68 Глава 1. Архитектура микроконтроллеров AVR и PIC Таблица 1.22. Выбор режима работы порта MSSP для интерфейса 12С SSPM3 SSPM2 SSPM1 SSPM0 Режим 0 1 1 0 Режим Slave, используется 7-разрядный адрес 0 1 1 1 Режим Slave, используется 10-разрядный адрес 1 0 0 0 Режим Master, частота = Fosc / (4 (SSPADD + 1)) 1 0 1 1 Режим Master с программным управлением 1 1 1 0 Режим Slave, используется 7-разрядный адрес 1 1 1 1 Режим Slave, используется 10-разрядный адрес 7 6 5 4________3 2________1_________О GCEN ACKSTAT ACKDT ACKEN RCEN PEN RSEN SEN Рис. 1.43. Регистр SSPCON2 микроконтроллеров PIC Назначение разрядов регистра SSPCON2: SEN — устанавливается в лог. 1 для создания условия начала передачи; RSEN — устанавливается в лог. 1 для создания повторных условий начала передачи; PEN — сбрасывается в лог. О для создания условия окончания передачи; RCEN — устанавливается в лог. 1 для разрешения режима приема; ACKEN — инициирует последовательность битов квитирования; ACKDT — устанавливается в лог. 1 для отправки подтверждения при приеме байта; ACKSTAT — устанавливается в лог. 1 при получении подтверждения приема от ведомого устройства. Модуль шины CAN Когда осуществляется обмен данными между несколькими устройствами по одной общей шине, существует вероятность возникновения самых разнообразных ошибок. Кроме того, возникают сложности с адресацией. В системе, состоящей из двух устройств, все очень просто: одно устройство передает данные, а другое оп- рашивает шину и принимает данные. В случае же с несколькими устройствами, требуется определить, какое именно из них передает данные и какому из уст- ройств, подключенных к общей шине. Все это обусловливает необходимость в не- котором протоколе обмена данными. Протокол определяет метод адресации, проверку ошибок и общий формат данных для всех устройств, использующих шину. Один из таких протоколов — протокол CAN (Controller Area Network), поддерживаемый рядом микроконтрол- леров семейства PIC18 и некоторыми последними разработками компании Atmel. В этой книге мы не будем подробно останавливаться на работе встроенных моду- лей CAN. Отметим только, что в главе 5 рассматривает пример использования библиотеки функций, предложенной компанией CCS, для организации обмена данными по шине CAN.
Аналого-цифровое преобразование 69 Аналого-цифровое преобразование Число в цифровой форме определяется на основании отношения входного на- пряжения к полному номиналу напряжения аналого-цифрового преобразователя (АЦП). Например, если на вход АЦП с номинальным напряжением 5 В подать на- пряжение 1 В, то на цифровом выходе появится число, соответствующее 1/5 - 0,2 разрешающей способности преобразователя. Так, если используется АЦП с раз- решением 8 бит, то максимальное возможное значение на его выходе 28- 1 = 255. Таким образом, напряжению 1 В на аналоговом входе соответствует 0,2 255 = 51 на цифровом выходе. Встроенные АЦП микроконтроллеров AVR и PIC имеют разрешение 10 бит и позволяют считывать напряжение на одном из восьми (в некоторых моделях — пяти) аналоговых входов (обычно — порт А). В микроконтроллерах AVR для управления режимом АЦП используются два регистра: регистр управления ADCSR (рис. 1.44) и регистр мультиплексирования ADMUX (определяет, какие из восьми входов порта А являются аналоговыми). 7 6 5 4 3 2 1 0 ADEN ADSC ADFR ADIF ADIE ADPS2 ADPS1 ADPSO Рис. 1.44. Регистр ADCSR микроконтроллеров AVR Назначение разрядов регистра ADCSR: ADPS0-ADPS2 — выбор коэффициента деления тактовой частоты (табл. 1.23); чем выше частота работы АЦП (производная от частоты сис- темной синхронизации), тем ниже эффективное разрешение, поэтому сле- дует устанавливать коэффициент деления; ADIE — разряд маскирования прерывания от АЦП (1 — по окончанию преобразования разрешено прерывание); ADIF — флаг прерывания от АЦП (устанавливается аппаратно по оконча- нию цикла преобразования); ADFR — лог. 1 в этом разряде переводит АЦП в несинхронизированный режим работы — обычно АЦП работает в режиме прерывания, чтобы про- цессор каждый раз не ожидал завершения медленно протекающего преоб- разования, однако в несинхронизированном режиме АЦП выполняет пре- образование постоянно, как можно быстрее (на период такого преобразо- вания должны быть запрещены все прерывания); ADSC — флаг начала преобразования; ADEN — флаг разрешения использования АЦП. Таблица 1.23. Выбор коэффициента деления частоты системной синхронизации для тактирования АЦП микроконтроллеров AVR ADPS2 ADPS1 ADPSO Коэффициент деления 0 0 0 1 0 0 1 2 0 1 0 4 0 1 1 8 1 0 0 16
70 Глава 1. Архитектура микроконтроллеров AVR и PIC Таблица 1.23. Окончание ADPS2 ADPS1 ADPS0 Коэффициент деления 1 0 1 32 1 1 0 64 1 1 1 128 Таким образом, в общем случае процесс аналого-цифрового преобразования в микроконтроллерах AVR протекает следующим образом: 1. Установить в лог. 1 разряды регистра ADMUX, соответствующие аналоговым входам. 2. Установить разряды 0-2 регистра ADCSR для выбора коэффициента деления частоты системной синхронизации. 3. Установить в лог. 1 разряд ADIE для разрешения режима прерывания. 4. Установить в лог. 1 разряд ADEN, чтобы разрешить использование АЦП. 5. Установить в лог. 1 разряд ADSC, чтобы начать преобразование. 6. Результат преобразования сохраняется в регистровой паре ADCL, ADCH. В микроконтроллерах PIC для управления работой встроенного АЦП также используются два регистра: ADCONO (рис. 1.45) и ADCON1 (рис. 1.46). 7 6 5 4 3 2 1 0 ADCS1 ADCS0 CHS2 CHS1 CHS0 GO/DONE ADIF ADON Рис. 1.45. Регистр ADCONO микроконтроллеров PIC Назначение разрядов регистра ADCONO: ADON — флаг разрешения использования АЦП; ADIF — разряд запроса на прерывание по окончанию преобразования; GO/DONE — установка этого разряда в лог. 1 активизирует АЦП; по окон- чанию преобразования автоматически сбрасывается в лог. 0; CHS0-CHS2 — выбор аналогового входа порта А (назначение этих разря- дов зависит от типа микроконтроллера); ADCS0-ADCS1 — выбор рабочей частоты АЦП (табл. 1.24). Таблица 1.24. Выбор рабочей частоты АЦП микроконтроллеров PIC ADCS1 ADCS0 Частота 0 0 Частота системной синхронизации / 2 0 1 Частота системной синхронизации / 8 1 0 Частота системной синхронизации / 32 1 1 Тактирование от встроенного RC-осциллятора АЦП (250 кГц) Максимальная допустимая рабочая частота АЦП микроконтроллеров PIC — 625 кГц. Это следует учитывать при выборе коэффициента деления частоты сис- темной синхронизации. ПРИМЕЧАНИЕ Использование внутреннего RC-осциллятора АЦП не рекомендуется для микроконтролле- ров с частотой системной синхронизации выше 1 МГц.
Аналого-цифровое преобразование 71 7 6 5 4 3 2 1 О ADFM — — — PCFG3 PCFG2 PCFG1 PCFG0 Рис. 1.46. Регистр ADCON1 микроконтроллеров PIC Назначение разрядов регистра ADCON1: PCFG0-PCFG3 — разряды конфигурации — определяют, какие выводы порта АЦП являются аналоговыми входами, а какие используются для по- дачи опорных напряжений Vref+ и Vref_ ; наиболее распространенная конфи- гурация: 0000, которая определяет все 8 выводов аналоговыми входами, а в качестве опорных напряжений — напряжения VDD и Vss; другие значения разрядов PCFG0-PCFG3 варьируются в зависимости от типа микрокон- троллера; ADFM — выбор разрешения преобразования: 1 — 10 бит; 0 — 8 бит. Результат преобразования сохраняется в регистровой паре ADRESL, ADRESH, причем в регистре ADRESH используются только младшие два разряда в случае преобразования с разрешением 10 бит. Таким образом, в общем случае процесс аналого-цифрового преобразования в микроконтроллерах PIC протекает следующим образом: 1. Определить с помощью разрядов регистра ADCON1 конфигурацию преобра- зования. 2. Установить разряды 6-7 регистра ADCON0 для выбора рабочей частоты АЦП. 3. Установить разряды 3-5 регистра ADCON0 для выбора аналогового канала. 4. Установить в лог. 1 разряды ADON и GO регистра ADCON0, чтобы начать преобразование. 5. Дождаться окончания преобразования (лог. 0 в разряде DONE регистра ADCON0) и считать результат из регистровой пары ADRESLL, ADRESH. Встроенный аналоговый компаратор В ряде моделей микроконтроллеров AVR и PIC используется встроенный ана- логовый компаратор напряжения, сравнивающий входное напряжение на двух входах. Как только напряжение на неинвентирующем входе станет больше, чем на инвентирующем, на выходе компаратора устанавливается лог. 1. Часто аналого- вый компаратор используют для сравнения некоторого входного напряжения с опорным (рис. 1.47). Для активизации компаратора требуется предварительно перевести соответст- вующие выводы микроконтроллера в режим входов. Время срабатывания компа- раторов небольшое, поэтому с их помощью можно быстро формировать ответные реакции на изменение соотношений входных напряжений. В микроконтроллерах AVR входам компаратора AIN0 и AIN1 обычно соот- ветствуют разряды 0/1 или 2/3 порта В. В микроконтроллерах PIC конфигурация входов компаратора (вывода порта А) настраивается с помощью разрядов регист- ра CMCON, о чем речь пойдет чуть позже. В микроконтроллерах AVR для управления работой аналогового компаратора используется регистр ACSR (рис. 1.48), расположенный в области ввода/вывода по адресу 0x08 (адрес 0x2 8 в SRAM).
72 Глава 1. Архитектура микроконтроллеров AVR и PIC Рис. 1.47. Принцип работы аналогового компаратора напряжений 7 6 5 4 3 2 1 О ACD — АСО ACI ACIE ACIC ACIS1 ACIS0 Рис. 1.48. Регистр ACSR микроконтроллеров AVR Назначение разрядов регистра ACSR: ACD — если установлен в лог. 1, то питание аналогового компаратора от- ключено; АСО — напрямую связан с выходом аналогового компаратора; ACI — флаг прерываний от аналогового компаратора — устанавливается в лог. 1, когда наступает событие, определенное разрядами ACIS1 и ACIS0; ACIE — разряд разрешения прерывания от аналогового компаратора; ACIC — разряд разрешения захвата на входе аналогового компаратора; для того чтобы можно было вызвать прерывание по захвату, оно должно быть разрешено разрядом TICIE1 в регистре TIMSK; ACIS1, ACIS0 — устанавливают вид события на выходе аналогового ком- паратора, которое должно вызвать прерывание его работы (табл. 1.25). Таблица 1.25. Вид событий для вызова прерывания от аналогового компаратора ACIS1 ACIS0 Вид прерывания аналогового компаратора 0 0 Прерывание при изменении состояния выхода (разряда АСО) 0 1 Не используется 1 0 Прерывание по ниспадающему фронту на выходе аналогового компаратора (напряжение на AIN1 больше, чем на AINO) 1 1 Прерывание по нарастающему фронту на выходе аналогового компаратора (напряжение на AINO больше, чем на AIN1) В микроконтроллерах PIC могут использоваться два аналоговых компарато- ра— в частности, в серии Р1С16С62х. В этом случае для управления их работой предназначен регистр CMCON (рис. 1.49).
Аналого-цифровое преобразование 73 7 6 5 4 3 2 1 О C2OUT сюит — CIS СМ2 СМ1 СМО Рис. 1.49. Регистр CMCON микроконтроллеров серии Р1С16С62х Назначение разрядов регистра CMCON: СМ0-СМ2 — выбор режима работы компаратора (табл. 1.26); CIS — разряд управления входным коммутатором компаратора (табл. 1.26); C1OUT — выходной сигнал компаратора 1; C2OUT — выходной сигнал компаратора 2. Таблица 1.26. Конфигурация входов компараторов напряжения в серии PIC16C62X CIS СМ2 СМ1 СМО Компаратор 1 Компаратор 2 Вход + Вход- Вход + Вход- X 0 0 0 RA0 RA3 RA2 RA1 0 0 0 1 RA2 RAO RA2 RA1 1 0 0 1 RA2 RA3 RA2 RA1 0 0 1 0 Vref RAO Vref RA1 1 0 1 0 Vref RA3 Vref RA2 X 0 1 1 RA2 RAO RA2 RA1 X 1 0 0 RA3 RAO RA2 RA1 X 1 0 1 — — RA2 RA1 X 1 1 0 RA2 RAO RA2 RA1 X 1 1 1 - — — —
Глава 2 Компиляторы и средства разработки Программное обеспечение для микроконтроллеров AVR В этой книге используются два бесплатных программных пакета для микро- контроллеров AVR: компилятор WinAVR (www. sourcef orge . net) и среда от- ладки программ AVRStudio 4 от компании Atmel. е Установочные пакеты этих средств находятся на прилагаемом к книге компакт-диске в папке Tools\AVR. Перечислим основные средства, входящие в состав этих пакетов: WinARV — компилятор, который представляет собой набор инструмен- тальных средств, предназначенных для программирования RISC- микроконтроллеров семейства AVR на языке С. Он был создан трудом множества добровольцев, посвятивших свое свободное время тому, чтобы бескорыстно помогать другим. В комплект поставки входит компилятор GNU GCC для языков С и C++. Programmers Notepad — еще одна замечательная бесплатная программа от SourceForge, входящая в комплект поставки компилятора WinAVR. Она будет использоваться в этой книге для разработки программ. AVRStudio — среда, созданная компанией Atmel (также распространяется бесплатно), которая используется для загрузки созданных программ в тот или иной программатор, а также эмуляции выполнения программ на том или ином микроконтроллере. ПРИМЕЧАНИЕ Для различных моделей микроконтроллеров существуют различные программаторы, кото- рые имеют свои особенности сборки и подключения к компьютеру, поэтому в этой книге бу- дет рассмотрена только эмуляция программ в среде разработки. Разработка и компиляция программ в Programmers Notepad Прежде всего, рассмотрим основной инструмент программирования на С для микроконтроллеров AVR, который будем использовать в этой книге, — програм- му Programmers Notepad, входящую в состав пакета WinAVR. По умолчанию, по- сле установки WinAVR ее ярлык будет размещен на Рабочем столе Windows. В любом случае, Programmers Notepad можно запустить по соответствующей ко- манде меню Пуск ► Все программы ► WinAVR. Интерфейс Programmers Notepad прост и интуитивно понятен: за исключени- ем меню Tools, все остальные меню — обычные для любого приложения Windows
Программное обеспечение для микроконтроллеров AVR 75 (File, Edit, View, Window, Help), и потому на них мы останавливаться не будем. Окно Programmers Notepad при первом запуске показано на рис. 2.1. Рис. 2.1. Окно Programmers Notepad при первом запуске Внешние средства компиляции и синтаксического анализа активизируются через меню Tools (если в среде разработки открыт какой-либо файл с исходным кодом). Для того чтобы добавить в это меню команду для создания файлов, ис- пользуемых при эмуляции в среде AVRStudio, необходимо выполнить команду Tools ► Options и в диалоговом окне Options выбрать категорию параметров Tools (рис. 2.2). Option» General Visual Help Style ; Schemes files •'••• New files In many cases It may be useful to set up external tools such as compilers or syntax checkers, You can set these tools up here. ЙЙЙййфмопе - GlobaiToois)' Alternate Files _______________________LfiffliiMPii [ЙпАУР]'Mate 55________make.exe [WinAVR] Make Clean make.exe [WinAVR] Program make.exe I Params BiBita clean program note: The first item in any list will be the default tool, Cancel Рис. 2.2. Внешние средства компиляции и синтаксического анализа
76 Глава 2. Компиляторы и средства разработки Для того чтобы добавить новый элемент в список, нажмем кнопку Add и в диалоговом окне Edit Tool установим следующие параметры (рис. 2.3): Output!-------~~------------------------- 1*7 Capture output? juse the main output window. -SI Г” Clear output before runnlrxj? Use .the built-in error parser, C Look for a custom pattern! p— exampie: %f:%l: i Pattern Symbols: %f: File Name %l: Line Number %c: Column L._............... -QK ' "I' Cancel- "'[i Рис. 2.3. Добавление средства создания файлов COFF для эмуляции программ в среде AVR Studio Name— [WinAVR] Make Extcoff— имя средства; Command — make. exe — компиляция и сборка программы осуществляет- ся с помощью средства make. ехе, расположенного в папке \WinAVR\utils\ bin, на основании параметров компиляции (формат вы- ходного файла, тип микроконтроллера и др.), указанных в файле makefile (такой файл должен быть скопирован в ту же папку, что и исходный код программы — это будет показано чуть позже); Folder — %d — это означает, что выходные файлы размещаются в той же папке, что и исходные; Parameters — extcoff — параметр, передаваемый файлу make. ехе; здесь возможны следующие варианты: • all — на выходе получают файлы для загрузки в микроконтроллер; • clean — удаление всех выходных файлов; • cof f — преобразование файлов из формата ELF (Executable and Linkable Format) в AVR COFF (Common Object File Format) для использования в среде AVR Studio 3.x или VMLAB; • extcoff — преобразование файлов из формата ELF в расширенный формат AVR COFF для использования в среде AVR Studio 4.x или вы- ше) — нас интересует именно этот вариант; • program — загрузка НЕХ-файла в микроконтроллер с помощью специ- альной программы avrdude. ехе, входящей в пакет WinAVR; • filename . s — просто компиляция файла filename . с в ассемблерный код; Save — Current File — автоматическое сохранение при компиляции только текущего файла с исходным кодом;
Программное обеспечение для микроконтроллеров AVR 77 Output — Use the main output window — использование для вывода сообще- ний о ходе и результатах компиляции общего, а не индивидуального окна Output (расположено вдоль нижнего края окна Programmers Notepad); Clear output before running? — этот флажок лучше сбросить, чтобы содер- жимое окна Output не очищалось перед очередным вызовом средства. Нажимаем кнопку ОК вначале в окне Edit Tool, а затем — в окне Options, чтобы сохранить изменения в списке подключаемых средств. Процесс компиляции в среде Programmers Notepad проиллюстрируем на при- мере простой программы, создающей эффект сигнала SOS с помощью светоинди- каторного табло, подключенного к микроконтроллеру AVR. Для создания исходного кода программы в среде Programmers Notepad можно воспользоваться одним из двух способов: выполнить команду меню File ► New ► C/C++, чтобы создать новый файл исходного кода, а затем ввести в него текст из листинга 2.1; после этого следует выполнить команду меню File ► Save (комбинация клавиш <Ctrl+S>) и сохранить созданный файл в отдельной папке под именем sos. с; предварительно скопировать с прилагаемого к книге компакт-диска на же- сткий диск папку Proj ects\AVR\S0S, а затем выполнить команду меню File ► Open (комбинация <Ctrl+O>) и открыть из скопированной папки файл SOS. с. Листинг 2.1. Файл SOS. с для микроконтроллеров AVR #include <avr/io.h> #include <avr/delay.h> void Pause(int ms) { PORTD = OxFF; //Все светодиоды отключены delay_loop_2(ms); //Задержка void P(void) PORTD = 0; delay_loop_2(5); Pause (5); //Включаем все светодиоды //Короткая задержка //Пауза с погасшими светодиодами void D(void) PORTD = 0; //Включаем все светодиоды delay_loop_2(20); //Длинная задержка Pause(5); //Пауза с погасшими светодиодами int main (void)
78 Глава 2. Компиляторы и средства разработки DDRD = OxFF; // Настройка порта D для вывода while(1) //Бесконечный цикл { Р(); Р(); Р(); //• • D(); DO; D(); //---- РО; РО; р(); //• . . Pause (100) ; } 2 ПРИМЕЧАНИЕ Программные элементы мы пока рассматривать не будем, поскольку это — материал сле- дующих глав. Этот пример используется только для изучения среды программирования. Перед компиляцией заглянем в файл makefile. Скопируйте его в папку с файлом SOS. с из папки \Projects\AVR\S0S, если еще этого не сделали. При ко- пировании этого файла из проекта в проект изменению подлежат только два поля: MCU —тип микроконтроллера, для которого создаются выходные файлы; format — формат загружаемого файла (используются значения srec, ihex, binary); target — имя исходного файла (без расширения . с). Предположим, программа SOS должна компилироваться для микроконтролле- ра Atmegal69 в формате ihex (читатель может использовать необходимые ему значения). В этом случае соответствующий фрагмент файла makefile будет вы- глядеть следующим образом: # MCU name MCU = atmegal69 # Output format, (can be srec, ihex, binary) FORMAT = ihex # Target file name (without extension). TARGET = SOS Теперь откомпилируем программу SOS. с. Для этого в среде Programmers Notepad выполним команду меню Tools ► [WinAVR] Make АН. Если в окне Output в нижней части Programmers Notepad не было выдано сообщений об ошибках, зна- чит компиляция прошла успешно и можно создать файл для эмулятора AVR Stu- dio. Для этого выполним команду меню Tools ► [WinAVR] Make Extcoff. В результа- те в одной папке с файлом SOS . с будет создан файл SOS . cof. Эмуляция и отладка программ в среде A VR Studio 4 AVR Studio 4 — это интегрированная среда разработки (IDE, Integrated Devel- opment Environment), которая очень удобна для отладки AVR-приложений в опе- рационных системах Windows 9x/Me/NT/2000/XP. Эта среда предлагает интер- фейс программного имитатора и внутрисхемного эмулятора для восьмиразрядных
Программное обеспечение для микроконтроллеров AVR 79 микроконтроллеров AVR RISC. Кроме того, AVR Studio поддерживает набор раз- работчика STK500, позволяющий программировать AVR-устройства, а также но- вый встроенный эмулятор JTAG. Поскольку описание эмуляторов, которые сами по себе не являются составной частью AVR-Studio, а должны приобретаться у компании Atmel, выходит за тема- тические рамки этой книги, то в дальнейшем ограничимся только рассмотрением интегрированного имитатора. После установки AVR Studio по умолчанию на Рабочем столе Windows поя- вится соответствующий ярлык. В любом случае, эту среду можно запустить по команде из меню Пуск ► Все программы ► Atmel AVR Tools. При первом запуске AVR Studio на экране появится окно приветствия (рис. 2.4). Рис. 2.4. Окно приветствия AVR Studio 4 С помощью этого окна можно выполнить одну из трех операций: создать новый проект “с нуля” — кнопка Create New Project; открыть какой-либо файл . cof или . hex с диска — кнопка Open; открыть один из проектов, которые использовались последними — для этого следует выбрать один из элементов в расположенном ниже списке. Для примера, загрузим для эмуляции созданную в предыдущем подразделе программу SOS. с. Для этого в окне приветствия нажмем кнопку Open и откроем файл SOS. cof. В результате будет предложено выбрать тип эмулятора и микро- контроллера (рис. 2.5). В данном случае нас интересует интегрированный имита- тор AVR Simulator и микроконтроллер Atmega169 (читатель может использовать другой необходимый ему тип микроконтроллеров). Теперь можно нажать Finish, чтобы перейти к эмуляции и отладке программы. ПРИМЕЧАНИЕ Для того чтобы окно приветствия при последующих запусках AVR Studio не отображалось, в нем следует сбросить флажок Show this dialog on open. В таком случае все операции по созданию и загрузке проектов выполняются с помощью команд меню File и Project, а окно выбора эмулятора и типа микроконтроллеров, соответствующее рис. 2.5, открывается по команде меню Debug ► Select Platform and Device.
80 Глава 2. Компиляторы и средства разработки Welcome to AVR Studio 4 JTAGICE mkll ICE40 ICE 50 J TAG ICE AVR Simulator ICE200 Select debug platform and device: I Debug Platform:_________________ Devices :]ATmega168 Atmegai69j AT mega32 AT mega323 AT mega329 ATmega48 AT mega64 AT megaB AT mega8515 AT mega8535 AT mega88 Help | < ,Вк1г| | Finish | Cancel | ver, 4.09.338 Рис. 2.5. Выбор эмулятора и типа микроконтроллеров Главными окнами AVR Studio являются окно исходного кода и Workspace (рис. 2.6). ' E) Eite ErojKt £dt yjsw lools Cebud M»dow tfelp : d i»u0e »«ее й «i йл % ‘л ;a Z1 * м * т ш Workspace *• x». > » > x -n-vy--—— AVRStudlo - [d;\work\booKsXcfomK\woied3>\st>«/sw.<:l - ex: Л (t Ф f+j Register 0-lS & @ Register 16-31 it; Processor it; (=) Stack Monitor ;*• S I/OATMEGA169 | Vakue j Bii7 void D(void) PORTD = OxFF; _delay_Loop_2(20), Pause(5). «=£>|int main (void) DDRD = OxFF. while(l) P(). P(>; DO: DO: PO: PO: Pause(lOO): PO: DO: Насяцхл IM <> info • ^d:\wCTkthcokg\dtirmcVyojects\sos/so$.c v J Loaded plugin STK500 Loaded partfile: C:\Program Files\Atmel\AVR Tools\PartDescriptionFiles\ATmegal69.xml ; gh : AVR Simulator Please wait while configuring simulator... % « < ► h\Build ^Messages/ Findin Ffeg / Breakpoints / Tracepoints / CLlj_____________________________ ' , | ;; ATmegaf69 - AVR.Simulator Auto Stopped 0 Ln27,Coll Рис. 2.6. Окно AVR Studio с загруженным объектным файлом В окне исходно кода строка, подлежащая исполнению первой, обозначена желтой стрелкой. При этом следует учитывать, что исходный код извлечен из файла . cof или . hex, и потому редактированию не подлежит. Изменения следует вносить в среде Programmers Notepad, затем по-новому выполнять компиляцию и создание объектного файла, а затем — переключиться в AVR Studio. Если в AVR Studio был открыт тот же файл, то внешние изменения будут распознаны и на эк- ране появится предложение автоматически обновить содержимое окна исходного кода. Эту же операцию можно выполнить и вручную с помощью кнопки па- [Цй нели инструментов Reload Object File. IS
Программное обеспечение для микроконтроллеров AVR 81 Окно Workspace Окно Workspace, расположенное на рис. 2.6 слева от окна исходного кода, предоставляет доступ к 32-м рабочим регистрам, к параметрам процессора (счет- чик команд, указатель стека, регистры двойной длины X, Y и Z, частота осцилля- тора и др.), к значениям стека, а также портам ввода/вывода выбранного для эму- ляции микроконтроллера. К примеру, можно отредактировать содержимое какого- либо рабочего регистра как до, так и в процессе выполнения программы. Для это- го следует выбрать требуемый элемент в окне Workspace, дважды щелкнуть мы- шью на значении в поле Value и ввести требуемое значение в диалоговом окне Edit (рис. 2.7). Workspace - z * - * - ' S Ж Register 0-15 ИЗ 0 0x00 : [3 1 0x00 :..(=3 2 0x00 С 1=1 4 0x00 (3 5 0x00 Я Project " Рис. 2.7. Изменение значения в рабочем регистре 3 Workspace Name____________ //jS Processor i.* i V » L . « Program Counter 0x0000 Stack pointer Cycle Counter X-register V-register Z-register Frequency Stop Watch 0x04FD | Value I Bit? 0x0100 0x04FF 0x0104 4.0000 MHz 0.00 us При этом значение можно вводить в шестнадцатеричном, десятичном, вось- меричном или двоичном формате, что выбирается с помощью соответствующих переключателей. Особый интерес на этапе отладки представляет собой ветвь параметров процессора (рис. 2.8). Представлен- ные здесь элемент позволяют про- сматривать и изменять содержимое программного счетчика, указателя стека, счетчика циклов во время вы- полнения программы, регистров двойной длины X, Y и Z, а также час- тоту кварцевого осциллятора и пока- о _ п ... . „ г Рис. 2.8. Параметры процессора в окне Workspace зания таймера отсчета времени. Значение счетчика команд можно изменить с помощью окна, представленного ранее на рис. 2.7. При этом откроется окно Disassembler с кодом программы на ас- семблере и текущим будет выделена команда с соответствующим адресом (это же окно можно открыть в любой момент времени, выполнив команду меню View ► Disassembler). Для сброса в нуль таймера отсчета времени следует дважды щелкнуть мышью на элементе Stop Watch. Подобный таймер удобно использовать для того, чтобы с точностью до сотых долей микросекунд определять, сколько времени ушло на выполнение некоторого фрагмента программы. Самый нижний элемент в окне Workspase позволяет контролировать работу устройств ввода/вывода микроконтроллера, выбранного для эмуляции (рис. 2.9). Архитектура микроконтроллеров AVR рассматривается в предыдущей главе. 6 — 6-767
82 Глава 2. Компиляторы и средства разработки К примеру, с помощью элемента PORTD мы можем увидеть созданную ранее программу SOS. с в действии (рис. 2.10). Для этого достаточно за- пустить ее на выполнение командой меню Debug ► Auto Step (комбинация клавиш <Alt+F5>) или соответст- вующей кнопкой панели инет- рументов Debug. LSz. ПРИМЕЧАНИЕ Команда Auto Step отличается от коман- ды Run (клавиша <F5>) тем, что в ходе выполнения программы после каждого шага обновляются окна AVR Studio (в случае команды Run — не обновляют- ся). Для того чтобы прервать выпол- нение программы, следует выпол- нить команду меню Debug ► Break (комбинация клавиш <Ctri+F5>) или Kame [ Value | Bits | Address э^/омйЁёмб5 ' ® О AD_CONVERTER ±. •£> ANALOG-COMPARATOR Й-(=) BOOTJ.OAD Й-[=| CPU Л (=) EEPROM ® EXTERNAL .INTERRUPT S JTAG S ill MISC ffi =S PORTA fi=§ PORTB ffi 'S PORTC , , A -=g PORTD ' ••=£ PORTD 0x00 □□□□□□□□ OB (28) DDRD OxFF 0A (2A) j PIND 0x00 □□□□□□□□ 09(29) ® =£ PORTE 1 S =2 PORTF / ® portg / s Is spi || а О TIMER_COUNTER_0 =;i| a © TIMER.COUNTER_1 <:Д S-® TIMEP _COlINTER..2 Я USARTO Ш Si-E USI Я Щ WATCHDOG ® Project :pOnnfo нажать соответствующую кноп- ку панели инструментов Debug. Рис. 2.9. Элементы контроля за работой устройств ввода/вывода микроконтроллера AVR &=£ PORTC В =£ PORTD - =2 PORTD ; DDRD ; ••=§ PIND S~=s PORTE OxFF OB (2B) OxFF ОД (2Д) OxFF 09 (29) Рис. 2.10. Программа SOS отключает все “светодиоды", подключенные к выводам порта D ВНИМАНИЕ! Поскольку эмулятор выполняет программу в виртуальной среде, она работает значительно медленнее, чем в реальном микроконтроллере (особенно, выполнение функций задержки, наподобие _delay_.loop__2 из программы sos. с). К примеру, для реальной программы sos, загружаемой в микроконтроллер, задержки для функции _delay_loop_2 должны иметь значения в 10000 раз больше. Учитывайте это обстоятельство при отладке про- грамм. Окна Memory Окно Memory позволяет пользователю при необходимости контролировать или изменять некоторую область памяти микроконтроллера AVR, например, па- мять программ, память EEPROM, память данных, память ввода/вывода. Одновре- менно могут быть открыты сразу несколько окон Memory — для этого использу- ются команды меню View (рис. 2.11). Тип области памяти выбирают с помощью раскрывающегося списка, распо- ложенного в верхнем левом углу окна Memory. В примере на рис. 2.11 для верхне- го окна была выбрана память программ, а для нижнего — память данных.
Программное обеспечение для микроконтроллеров AVR 83 Рис. 2.11. Два окна Memory AVR-Studio С помощью кнопки 8/16, расположенной справа от раскрывающегося списка, а также команд контекстного меню 1 Byte и 2 Byte пользователь может переклю- чаться между режимами разбивки дампа памяти на одно- и двухбайтные фрагмен- ты, а с помощью кнопки abc — отобразить/скрыть дополнительную колонку с ASCII-значениями отображенной области памяти. Поле Address служит для ото- бражения и ввода адреса ячейки памяти, на которой в данный момент установлен курсор. Количество колонок со значениями ячеек памяти выбирается с помощью раскрывающегося списка Cols (если в этом списке выбран элемент Auto, то коли- чество колонок выбирается автоматически в зависимости от ширины окна Memory). Содержимое ячейки памяти в окне Memory можно легко изменить. Введенные значения расцениваются как шестнадцатеричные числа, при этом новое значение попадает в ячейку памяти сразу же после каждого нажатия клавиши. Если это не- желательно (например, в случае применения эмулятора при доступе на запись к регистру UDR приемопередатчика UART инициируется новая передача, хотя в этом случае байт еще неполный), то новое значение можно альтернативно вво- дить в специальном диалоговом окне (см. рис. 2.7), которое открывается по двой- ному щелчку мыши на соответствующей ячейке памяти. В этом случае вводимое значение записывается в ячейку только тогда, когда в окне Edit будет нажата кнопка ОК (клавиша <Enter>). Значения, которые изменились с момента последней операции отладки, ото- бражаются красным шрифтом. Адреса восьмиразрядных ячеек памяти и ASCII- символы выделены серым фоном, а адреса 16-разрядных ячеек памяти — голубым фоном. Окно Register Окно Register (рис. 2.12) показывает содержимое 32-х рабочих регистров мик- роконтроллера, которое обновляется после выполнения каждой команды. Оно от- крывается по соответствующей команде меню View (или при нажатии комбинации клавиш <Alt+0>). Значения, изменившиеся в результате последней отладочной операции (на- пример, при пошаговом проходе или в результате достижения точки останова) отображаются красным шрифтом. 6*
84 Глава 2. Компиляторы и средства разработки I Register :RQO = 0x00 R01= 0x00 R0£ = 0x00 R03 = 0x00 iR04= 0x00 R05 = 0x00 R0 6 = 0x00 R07 = 0x00 >R08« 0x0 0 R09 = 0x00 R10 = 0x00 Rll= 0x0 0 :R12 = 0x0 0 R13= 0x00 R14= 0x0 0 R15 = 0x00 R16 = 0x00 R17s 0x01 R18 = 0x00 R19 = 0x00 :R20 = 0x0 0 R21= 0x0 0 R22 = 0x00 R23 = 0x00 R24= 0x00 R25== 0x00 0x00 R27= 0x01 ;R28 = OxFF R29 = 0x04 R30 = 0x04 R31= 0x01 Рис. 2.12. Окно Register среды AVR Studio Для того чтобы изменить содержимое регистра, следует остановить выполне- ние программы и сделать двойной щелчок мышью на выбранном регистре. Пра- вила изменений значений в регистрах аналогичны правилам изменения значений в окнах Memory. Окно Watch Окно Watch (рис. 2.13) служит для отображения типов, значений и адресов та- ких объектов как, например, переменные в программе на С. Оно открывается по соответствующей команде меню View (или при нажатии комбинации клавиш <Alt+l>). Это окно состоит из четырех колонок. В первой указано имя объекта, за которым ведется наблюдение, во второй — тип объ- екта (Integer, unsigned Char и т.д.), в треть- ей — текущее значение, а в четвертой — адреса объектов. Пользователь может до- бавлять новые объекты в окно Watch по команде контекстного меню Add Item или же по команде контекстного меню Add to Watch окна исходного кода. В последнем случае в список Watch будет добавлен элемент кода, на котором установлен кур- сор. Элементы окна Watch можно удалять по одному с помощью команды контек- стного меню Remove Selected Item или все сразу по команде контекстного меню Remove All Items. Рис. 2.13. Окно Watch среды AVR Studio Отладка программы Под отладкой подразумевают пошаговое выполнение программы с контролем содержимого регистров микроконтроллера (проверка на низком уровне) и пере- менных (проверка на программном уровне). Для отладки программ в AVR Studio используют команды меню Debug и кнопки одноименной панели инструментов. Прежде, чем рассмотреть эти команды имеет смысл разъяснить такое понятие как “точка прерывания”. Точка прерывания (breakpoint) — это строка исходного кода, на которой работа программы приостанавливается. Таких точек (обознача- ются коричневым кружком слева от строки) может быть установлено столько же, сколько эффективных строк в программе. Для установки/удаления точки преры- вания в текущей строке служит команда меню Debug ► Toggle Breakpoint (клавиша <F9>) или соответствующая кнопка панели инструментов Debug. Для удаления всех расставленных в программе точек прерывания служит команда меню Debug ► Remove Breakpoints или кнопка Clear all breakpoints панели инструментов Debug. Для последовательного перехода от одной точки
Программное обеспечение для микроконтроллеров AVR 85 прерывания к другой используется команда меню Debug ► Next Breakpoint или комбинация клавиш <Ctrl+F9>. Для перехода в режим отладки используются следующие команды меню Debug: Run, Auto Step — переход в режим отладки происходит, если встречается точка прерывания; 3 Step Into (клавиша <F11>) — выполняет текущую команду с заходом в подпрограммы (все окна обновляются); Step Over (клавиша <F10>) — выполняет текущую команду без захода в подпрограммы (все окна обновляются); Step Out (комбинация клавиш <Shift+Fl 1>) — запускает программу и выполняет ее до тех пор, пока не встретится окончание текущей под- ¥ ¥ программы; если ход выполнения находится в области основной програм- мы, то программа будет выполняться до тех пор, пока не будет остановле- на пользователем командой Break или не встретит точку прерывания; Run То Cursor (комбинация клавиш <Ctrl+F 10>) — запускает про- грамму, которая выполняется до тех пор, пока не будет достигнута позиция курсора в окне исходного кода; если встречается точка останова, то выполнение программы не останавливается; если позиция курсора не достигается никогда, то программа выполняется до тех пор, пока не будет остановлена командой Break. После выполнения команды все окна обнов- ляются. Настройка параметров имитатора Для того чтобы выбрать имитацию работы конкретного микроконтроллера, а также его рабочую частоту, служит диалоговое окно Simulator Options (рис. 2.14), которое открывается по команде меню Debug ► AVR Simulator Options (комбинация клавиш <Alt+O>). Рис. 2.14. Диалоговое окно Simulator Options
86 Глава 2. Компиляторы и средства разработки Допустимые параметры выбранного микроконтроллера отображаются в тек- стовом поле, расположенном справа. Программное обеспечение для микроконтроллеров PIC В этой книге используется два программных пакета для микроконтроллеров PIC: компилятор с языка С CCS-PICC PCW 3.17 и распространяемая бесплатно среда разработки MPLAB 7.31 от компании Microchip, включающая в себя интер- фейс программирования и внутрисхемного эмулятора, а также средства отладки. , Установочные пакеты этих средств находятся на прилагаемом к книге компакт-диске в папке Tools\PIC (файл limitedPCWH и папка MPLAB731). Компилятор CCS-PICC PCW разработан для запуска в среде Windows 95/98/Me/NT4/2000/XP. Прилагаемый к книге компакт-диск содержит урезанную версию компилятора, который накладывает ограничения на размер исходного компилируемого файла и работает только с двумя типами микроконтроллеров PIC. В этой книге используется версия PCW компилятора CCS-PICC С, способная работать с 12-, 14- и 16-разрядными кодами операций. Разработки и компиляция программ в среде CCS-PICC Как и рассмотренный в предыдущем разделе WinAVR, компилятор CCS-PICC доступен через его интегрированную среду разработки (IDE), позволяющую поль- зователю создавать проекты из одно или нескольких файлов исходного кода, на- страивать опции компилятора, а также компилировать исходный код в исполняе- мые файлы, предназначенные для загрузки в целевой микроконтроллер. По умолчанию, после установки CCS-PICC на Рабочем столе Windows будет размещен ярлык PIC С Compiler, который и используется для запуска среды разра- ботки. В любом случае, IDE CCS-PICC можно запустить по команде меню Пуск ► Все программы ► PIC-C ► PIC С Compiler. Окно среды разработки CCS-PICC при первом запуске показано на рис. 2.15. Рис. 2.15. Окно IDE CCS-PICC при первом запуске Создание проектов CCS-PICC вручную В среде разработки CCS-PICC в большинстве случаев речь идет не просто о файле с кодом на языке С, а о проекте, состоящем из одного или более файлов с
Программное обеспечение для микроконтроллеров PIC 87 исходным кодом программы (главный файл проекта имеет расширение . pjt). Но- вый проект можно создавать вручную с помощью команды меню Project ► New ► Manual Create или сгенерировать его автоматически с помощью мастера PIC Wiz- ard, который вызывается по команде меню Project ► New ► PIC Wizard. Оба метода создания нового проекта запрашивают у пользователя файла для проекта и целевое устройство. Если было выбрано создание проекта вручную, появится диалоговое окно, изображенное на рис. 2.16. В этом окне необходимо ввести имя главного исходного файла проекта (с расширением . с) и вы- брать целевое устройство. имя главного исходного Main File; Рис. 2.16. Диалоговое окно Manual Project Create ПРИМЕЧАНИЕ В версии компилятора CCS-PICC, поставляемого с этой книгой, можно использовать в качестве целевого устройст- ва микроконтроллеры PIC16F877, PIC16F877A и PIC18F458. Заголовочные файлы для устройств по умол- чанию устанавливаются в папку \Program Files\Р1СС\ ©еИйеЖййййййййй1йИй(1И^ Для того чтобы задать размещение файлов с описанием поддерживаемых ком- пилятором микроконтроллеров, отличное от выбранного по умолчанию, в окне Manual Project Create следует нажать кнопку Review/Edit Include File Dirs, в результа- те чего откроется диалоговое окно Include Files, показанное на рис. 2.17 (это же диалоговое окно можно также открыть в любой момент по команде меню Project ► Include Dirs или Options ► Include Dirs). Include I ill's > <4^ Include File Search Order: : C^rogram Fiies\PiCC\de',>ice3\ C:\Program FilesVPICC\drivers\ Move Up i~ Make this fct the default fc: new projects Рис. 2.17. Диалоговое окно Include Files ПРИМЕЧАНИЕ Суть заголовочных файлов с расширением .h подробно рассматривается в следующей главе. Сейчас только отметим, что это — внешние файлы описаний, подключаемые к про- граммному модулю с помощью директивы #include. Для того чтобы добавить новый путь в список, следует ввести его в располо- женном внизу поле (или найти с помощью диалогового окна поиска, нажав кнопку с изображением многоточия) и нажать кнопку Add. Для удаления текущего эле-
88 Глава 2. Компиляторы и средства разработки мента из списка предназначена кнопка Delete. После того как список путей поиска сформирован, можно нажать кнопку Save. После того как в диалоговом окне Manual Project Create введен имя главного файла проекта (с расширением . с) и выбран требуемый тип микроконтроллера, можно нажать кнопку Create. В результате в папке, указанной первой в списке из окна Include files (см. рис. 2.17), будет создан новый проектный файл с тем же име- нем, что и главный исходный файл, но с расширением .pjt, а также сам файл .с, содержащий автоматически сгенерированную заготовку для разработки програм- мы (пример — на рис. 2.18). Рис. 2.18. Создана заготовка для разработки программы на языке С Как видно из рис. 2.18, к проекту подключен внешний файл описаний для микроконтроллера PIC18F458, выбранного в диалоговом окне Manual Project Create. В случае использования мастера создания проектов PIC Wizard пользователь должны знать, как будет использоваться микроконтроллер, а также иметь базовые знания по архитектуре целевого микроконтроллера. Архитектура микроконтроллеров PIC рассматривается в предыдущей главе. В качестве примера создадим в отдельной папке новый файл с исходным ко- дом под именем SOS. с для микроконтроллера PIC18F458 и введем в него код, представленный в листинге 2.2. Этот пример — аналог рассмотренного выше для микроконтроллеров AVR. Листинг 2.2. Файл sos. с для микроконтроллера PIC18F458 #include <18F458.h> #use delay(clock=20000000) tffuses HS, NOWDT void Pause(int ms) { output_D(OxFF); delay_ms(ms); 1 //Все светодиоды отключены //Задержка void P(void) { output_D(0); delay ms(5); //Включаем все светодиоды //Короткая задержка
Программное обеспечение для микроконтроллеров PIC 89 Листинг 2.2. Окончание Pause(5); //Пауза с погасшими светодиодами void D(void) { output D ( 0) ; //Включаем все светодиоды delay_ms(20); Pause (5) ; } int main (void) { set tris D(OxFF); //Длинная задержка //Пауза с погасшими // Настройка порта D светодиодами для вывода while (1 ) t p 0 ; p () ; p () ; D(); D(); D () ; p 0 ; p 0 ; P 0 ; Pause(100); } } //Бесконечный цикл //. . . // //. . . ПРИМЕЧАНИЕ Программные элементы мы пока рассматривать не будем, поскольку это — материал сле- дующих глав. Этот пример используется только для изучения среды программирования. Файл SOS. с можно также скопировать с прилагаемого к книге компакт-диска из папки W ' Projects\PIC\SOS. Создание проектов CCS-PICC с помощью PIC Wizard Для запуска мастера PIC Wizard следует выполнить соответствующую коман- ду меню Project ► New, а затем указать размещение и имя главного файла проекта. В результате откроется окно мастера, состоящее из набора вкладок с параметрами проекта (рис. 2.19). ПРИМЕЧАНИЕ Внешний вид окна мастера может отличаться в зависимости от версии компилятора CCS- PICC и выбранного типа микроконтроллера. На вкладке General выбирается целевой микроконтроллер (раскрывающийся список Device), его рабочая частота (поле Oscillator Frequency), а также настраи- ваются общие параметры, наподобие разрядов предохранения, типа источника системной синхронизации, порогового напряжения для сброса и др. Для просмотра программного кода, который будет сгенерирован и добавлен в исходный файл в соответствии с параметрами на текущей вкладке, следует на- жать кнопку View Code Generated from this tab, расположенную в правом нижнем углу окна мастера. Фрагмент кода будет отображен в отдельном окне (рис. 2.20).
90 Глава 2. Компиляторы и средства разработки [New project ail General" Protect Name CAProoram Files\PlCC\DevrcL< , * , \ ^Function Generation................ • <♦ Opening brace on Ihe following line J C Opening brace on tea same line Device: IPIC18F458 OscilatorFrequency;. f 20 000 000 HZ ? Help й [Resislor/Capacttof 0$c Г* Code protected from reads Г O;sciUator switching is enabled. P Besetwhen bwwnoul detected [Brownout reset at 2.0V Г" power UpT Г Date EEPROM Code 11</&аШй1$цШег1^ Г” Debug mode for use with IQ) P LowVoftagePfogramrningonB^PIC1B]orB5(PlC18) /Г‘/Рг^#Ш§тоф^ ;:Г“; DataEEPROM write profited:' ?!/; P Boot block write protected.. . : Г” B oot Block Code Protected ^fignete^ andLCDXfirnersXPCHTimersXAn^^^ View Code : Generated i bom this lab; <««« Рис. 2.19. Вкладка General мастера PIC Wizard Inserted intn h file' /device tidc=8 /use delay(clock.-?.0000D00) /fuses RCJO. BROWNOUT. BORVZO. STVREN, LVP Рис. 2.20. Предварительный просмотр кода, сгенерированного мастером PIC Wizard На вкладке Communications (рис. 2.21) настраиваются параметры ввода/вывода для интерфейсов RS232 и 12С (скорость обмена данными, соответствующие линии портов ввода/вывода, проверка ошибок, режим Master/Slave и т.д.), а также акти- визируется/отключается аппаратный порт PSP. На вкладке SPI and LCD (рис. 2.22) пользователь может активизировать аппа- ратный интерфейс SPI и настроить соответствующие параметры обмена данными (режим Master/Slave, активный фронт тактового импульса, коэффициент деления частоты системной синхронизации, использование вывода /SS), а также использо- вание жидкокристаллического дисплея (LCD), если он доступен. На вкладке Timers (рис. 2.23) настраиваются параметры таймеров TMR0- TMR2, а также сторожевого таймера. Значение некоторых параметров: wdt on — включение сторожевого таймера; WDT Reset — период между сигналами сброса от сторожевого таймера; Source — выбор источника тактирования таймера TMR0: внутренний или внешний (по нарастающему или ниспадающему фронту сигнала); Frequency -— установка частоты в случае выбора внешнего источника так- тирования таймера TMR0;
Программное обеспечение для микроконтроллеров PIC 91 Resolution — разрешение таймера, влияющее на период между переполне- ниями счетного регистра (для таймеров TMR0 и TMR1 вычисляется авто- матически и отображается в поле Overflow); Interrupt Period — период между запросами на прерывание от таймера TMR2. Рис. 2.21. Вкладка Communications мастера PIC Wizard Рис. 2.22. Вкладка SPI and LCD мастера PIC Wizard
92 Глава 2. Компиляторы и средства разработки Рис. 2.23. Вкладка Timers мастера PIC Wizard Вкладке Analog (рис. 2.24) соответствует настройка встроенного аналого- цифрового преобразователя (конфигурация аналоговых входов, частота и разряд- ность преобразования). Рис. 2.24. Вкладка Analog мастера PIC Wizard
Программное обеспечение для микроконтроллеров PIC 93 На вкладке Other (рис. 2.25) активизируется модуль ССР и настраивается ре- жим его работы, а также выбирается конфигурация входов компараторов напря- жений. Рис. 2.25. Вкладка Other мастера PIC Wizard Вкладка Interrupts (рис. 2.26) позволяет с помощью набора флажков сделать доступным то или иное прерывание доступное на выбранном устройстве. Рис. 2.26. Вкладка Interrupts мастера PIC Wizard
94 Глава 2. Компиляторы и средства разработки Для каждого выбранного прерыва- ния в главную процедуру программы main () добавляется вызов соответст- вующей подпрограммы обработки пре- рывания enable_interrupts(), а тело самой подпрограммы размещается выше main (). (пример — рис. 2.27). На вкладке Drivers содержится спи- сок программных драйверов, доступных для выбранного микроконтроллера в компиляторе CCS-PICC. Выбор кон- кретного драйвера приводит к тому, что соответствующий заголовочный файл включается в проект и вызывается под- —з Inserted into .с file before meinQ: lfint_TIMtR() 'TIMERO_isrO Inserted into nfile in mninQ: i enabie_interrupts(INT_riMERO); enable_interrupts(global): Рис. 2.27. Пример программного кода, сгенери- рованного PIC Wizard на вкладке Interrupts программа для инициализации этого драйвера (по умолчанию размещены в папке \Program Files\PlCC\Drivers). Конфигурация выводов портов микроконтроллера настраивается на вкладке I/O Pins (рис. 2.28). При этом для каждого вывода возможны значения Input (вход), Output (выход), Input/Output (вход/выход), Analog (аналоговый) и Not used (не ис- пользуется). Каждому выводу в программе будут соответствовать идентификато- ры, указанные через запятую в столбце Identifiers. Если установить флажок Enable Pullups on port В, то вывода порта В будут сконфигурированы с подтягивающим сопротивлением. Рис. 2.28. Вкладка I/O Pins мастера PIC Wizard
Программное обеспечение для микроконтроллеров PIC 95 На последней вкладке мастера PIC Wizard — Header Files — с помощью флаж- ков можно включить в проект дополнительные заголовочные файлы, используе- мые, например, для работы со сроками или с числами с плавающей запятой. Открытие и добавление в проект файлов с исходным кодом Для того чтобы открыть существующий файл с исходным кодом программы, следует выполнить команду меню File ► Open (комбинация клавиш I <Ctrl+O>) или нажать кнопку Open на панели инструментов. Можно также \~~L воспользоваться командой Project ► Open All Files, по которой откроются все фай- лы, связанные со скомпилированным проектом. Для создания нового исходного файла используется команда меню File ► New. Каждому файлу в окне среды разработки соответствует отдельная вкладка. Для того чтобы закрыть текущую вкладку, следует выполнить команда меню File ► Close (комбинация клавиш <Ctrl+F4>) или же щелкнуть на вкладке правой кнопкой мыши и выбрать в контекстном меню команду Close. Для того чтобы полностью закрыть проект, следует выполнить команду меню File ► Close all, команду Project ► Close Project или нажать кнопку Close All l;w панели инструментов. — Компиляция проекта Для компиляции текущего проекта можно выполнить команду меню Compile ► Compile, нажать клавишу <F9> или нажать кнопку Compile панели Г7~ инструментов. ПРИМЕЧАНИЕ Перед компиляцией следует выбрать корректное значение серии микроконтроллеров PIC в раскрывающемся списке, расположенном в панели инструментов слева от кнопки Com- pile. В результате компиляции в папке размещения исходных файлов будут созда- ны еще несколько файлов с тем же именем, но другими расширениями: . cod — файл, используемый в среде отладки, наподобие MPLAB; . err — файл с перечнем ошибок (если были обнаружены); кроме того, первая обнаруженная ошибка будет выделена в исходном тексте програм- мы, а ее описание — отображено на красном фоне в строке состояния; . hex — объектный файл для загрузки в микроконтроллер; . 1st — файл листинга, в котором отображены соответствия между опера- торами на языке С и ассемблерными наборами команд; . sta — файл статистики по результатам последней компиляции; . sym — файл символов, содержащий адреса переменных в памяти RAM ; . tre -— дерево вызовов подпрограмм. Формат некоторых из этих файлов выбирают в диалоговом окне File Formats, которое открывается по команде меню Options ► File Formats (рис. 2.29). Формат отладочного файла выбирают с помощью группы переключателей Debug File; объ- ектного файла — Object File (восьми- или 16-тибитный . hex или двоичный); файла
96 Глава 2. Компиляторы и средства разработки с перечнем ошибок — Error File, файла листинга — List File. С помощью флажков в группе Other Files можно отменить создание файлов . sta и . tre. В группе Compile Window Up настраиваются параметры отображения окна с ре- зультатами компиляции (рис. 2.30). If ile f ormdts r Standard COD i Expanded .COD угУбШЭДйщй® r Etrot-Ffe"”""”:' ‘ C Original I Г* Standard: тД11теЩ1ё5Й'УЖ“'Ш Др: ИЙЙ&ЙШЗЖй ; Г' Scrach files ;йОШЙ1Игивя! МШЙИЛйй ййййг • ИЙШНоИ ' Г Old Microchip j УОЭДтЕ® . Cornpile Window IJp •; 1Жйд(ио.....Д-| i CCS PLH C Compiler, Version 3.1 / Ob Registered to: Limited. Evaluation Version I Project: c:\program files\picc\devices\2..c |Files: 3, Statements: 6, Time: 2 Sec, Lines: 353 (output files: ERR HEX SYM LST COD PJT |0 Errors, 0 Warnings, Time: 2 Seconds 2% I RAM: 0% Рис. 2.29. Диалоговое окно File Formats Рис. 2.30. Результаты работы компилятора CCS-PICC В раскрывающемся списке No Errors устанавливается значение задержки окна результатов на экране (в секундах) в случае отсутствия ошибок компиляции. Ва- рианту Hold Up соответствует постоянное отображение, пока пользователь сам не закрое окно результатов. Список On Error имеет тот же смысл, но для случая, когда были обнаружены ошибки: No — окно результатов компиляции не отображается; As above — задержка отображения как в случае отсутствия ошибок. Любой из файлов, созданных в результате компиляции проекта, можно быст- ро непосредственно в среде разработки с помощью одной из команд меню View: C/ASM List, Symbol Map, Call Tree, Statistics, Compiler Messages (файл .err), Binary File, COD Debug File. Оставшиеся команды меню View предназначены для просмот- ра файла спецификаций (команда Data Sheet), а также просмотра корректных обо- значений для использования в директивах #fuses (разряды предохранения) и #int_ (подпрограммы обработки прерываний) по тому или иному микроконтрол- леру Меню Tools Меню Tools содержит команды доступа к различным полезным утилитам. Рас- смотрим назначение некоторых из них: Device Editor — доступ к базе данных свойств каждого поддерживаемого микроконтроллера PIC (в урезанной версии компилятора эта команда не- доступна); Device Selector — выбор целевого микроконтроллера и его свойств (в уре- занной версии компилятора эта команда недоступна);
Программное обеспечение для микроконтроллеров PIC 97 File Compare — утилита сравнения файлов (исходных кодов или листин- гов) — если выбрано сравнение исходных файлов, то выполняется обычное построчное сравнение, если же выбрано сравнение листингов, то его мож- но настроить таким образом, чтобы адреса памяти не учитывались (рис. 2.31); установленный флажок Ignore case определяет игнорирование регистра букв; Numeric Converter — средство преобразования целых и вещественных чисел в десятичном представлении в шестнадцатеричное и наоборот (рис. 2.32); Serial Port Monitor — монитор последовательного порта для отладки встро- енных систем, предназначенных для обмена через интерфейс RS-232, RS- 442, RS-485. Рис. 2.31. Средство сравнения двух файлов Рис. 2.32. Средство преобразования чисел Эмуляция и отладка программ в среде Microchip MPLAB 7.31 Компилятор CCS-PICC спроектирован для работы совместно с отладчиком Microchip MPLAB, представляющий собой программный имитатор микрокон- троллеров PIC (включена поддержка внутрисхемных эмуляторов ICE и PICMASTER). Средство MPLAB можно бесплатно загрузить с Web-сайта компа- нии Microchip (www. microchip. com). В среде MPLAB используются файлы . cod, созданные компилятором CCS- PICC. Для сопряжения CCS-PICC с MPLAB версий 6 и 7 требуется установить до- полнение (plug-in), которое можно загрузить с Web-сайта www. ccsinfo. com. Соответствующий установочный файл mplab-ccs-plugin-install. ехе также нахо- дится на прилагаемом к книге компакт-диске в папке Tool s\PIC. Прежде чем запустить MPLAB, в среде CCS-PICC необходимо настроить раз- мещение этого средства эмуляции и отладки. Для этого следует выполнить коман- ду меню Options ► Debugger/Programmer и в открывшемся диалоговом окне указать в разделе Debuggers корректное размещение файла mplab.exe (пример — рис. 2.33). Теперь для запуска MPLAB из CCS-PICC достаточно просто выполнить ко- манду меню Tools ► MPLAB. 7 — 6-767
98 Глава 2. Компиляторы и средства разработки iDevice Programmer,Ли НрЬогь i Name: Command Line: fejMPLAB ss|iE\Program Files\Mictochip\MPLAB” IDE\core\mplab: ХР • Project file name XC-Pebug filename: Рис. 2.33. Настройка размещения средства MPLAB Рабочая область и проект MPLAB Для того чтобы перейти в режим имитации или отладки программы в среде MPLAB, для нее необходимо создать рабочую область и проект. Для этого лучше всего воспользоваться специальным мастером, который запускается в среде MPLAB по команде меню Project ► Project Wizard. В первом окне мастера следует только нажать кнопку Далее, после чего откроется окно выбора типа микрокон- троллера для имитации. На следующем этапе (рис. 2.34) выбирают программную среду, используемую для разработки отлаживаемых программ. В нашем случае в раскрывающемся спи- ске Active Toolsuite следует выбрать элемент CCS С Compiler for PIC12/14/16/18, и удостовериться, что в поле Location указан корректный путь к файлу ccsc. ехе. Рис. 2.34. Выбор средства разработки программ
Программное обеспечение для микроконтроллеров PIC 99 На следующем шаге указывается имя MPLAB-проекта и выбирается каталог его размещения, а в следующем окне мастера необходимо добавить в проект су- ществующие файлы с исходным кодом проекта CCS-PICC (рис. 2.35). Для этого требуемые файлы следует выделить в списке, расположенном слева, и нажать кнопку Add. Добавим, к примеру, созданный ранее файл SOS . с. Рис. 2.35. Выбор файла с исходным кодом программы В последнем окне мастера достаточно нажать кнопку Готово. Окно MPLAB с загруженным для отладки файлом sos. с показано на рис. 2.36. Рис. 2.36. Окно MPLAB 7.31 7*
100 Глава 2. Компиляторы и средства разработки Для сохранения проекта используют команду меню Project ► Save Project или Project ► Save Project As, а для сохранения рабочей области — команду File ► Save Workspace и File ► Save Workspace As. Компиляция под управлением MPLAB Проект можно откомпилировать с помощью подключенного компилятора CCS-PICC непосредственно в среде MPLAB. Для этого служит команда меню Project ► Compile (клавиша <F 10>). Окно состояния компилятора CCS-PICC откро- ется на период компиляции, а затем исчезнет. Для сбора данных, необходимых при эмуляции и отладке MPLAB требуется файл .cod или .cof. При вызове ком- пилятора CCS-PICC из MPLAB для него, по умолчанию, выходной файл — . cof, однако при подобном вызове он не создается. Вместо этого должен генерировать- ся файл . cod. Для того чтобы выбрать этот формат, следует в среде MPLAB выполнить ко- манду меню Project ► Build Options ► Project, в диалоговом окне Build Options пе- рейти на вкладку CCS С Compiler, в группе параметров Debug выбрать переключа- тель Expanded COD Format (рис. 2.37) и нажать кнопку ОК. Рис. 2.37. Диалоговое окно Build Options Теперь по завершению компиляции эмулятор MPLAB будет автоматически загружать файл .cod, созданный компилятором CCS. Настройка режима отладки/эмуляции Микроконтроллер для программной эмуляции выбирается на этапе создания проекта MPLAB, но этот выбор можно изменить позже, выполнив команду меню Configure Select Device и указав требуемое значение в поле Device диалогового окна Select Device (рис. 2.38).
Программное обеспечение для микроконтроллеров PIC 101 Рис. 2.38. Выбор микроконтроллера для программной эмуляции Для перевода MPLAB в режим программной эмуляции, необходимо выпол- нить команду меню Debugger ► Select Tool ► MPLAB SIM. В результате отобразится панель инструментов Debug с кнопками, используемыми для отладки. Для настройки параметров эмуляции следует выполнить команду меню Debugger ► Settings. В открывшемся диалоговом окне Simulator Settings особый ин- терес представляют вкладки Osc/Trace и Animation / Realtime Updates (рис. 2.39). Simulalui Setimqs punuldtoi SeUitiq* Uartl 10 | Animation I Realtime Updates | Limitations Osc/Trace | Break Options | SCL Options - Processor Frequency il SBp " J Г кнг \ C Hi t Trace Options............................................ •; . P Trace All Г 'greak on Trace Buffer Full —„_________________Ji OK I Отмена j Примени. Osc/Trace | Break Options | SCL Options Uartl 10 Animation/Realtime Updates | Limitations • Animate step time........................... p------------------: -Realtime watch------------------------------- P Enable Realtime watch updates l|l.. .... OK I Отмена ] B-umci-.1 Рис. 2.39. Параметры эмуляции На вкладке Osc/Trace можно выбрать частоту тактового осциллятора, а на вкладке Animation/Realtime Updates задать период анимационного прохода (выпол- нение программы с обновлением окон отладчика через строго определенные про- межутки времени) и период обновления окон при выполнении в реальном режиме времени. Если требуется, чтобы обновления окон отладчика происходили таким
102 Глава 2. Компиляторы и средства разработки образом, чтобы отражать информацию реалистично, значения этих периодов не следует выбирать большими. ВНИМАНИЕ! Если не установить флажок Enable Realtime watch updates, то при запуске программы в режиме реального времени окна отладчика вообще не будут обновляться. ПРИМЕЧАНИЕ Как и в случае с AVR Studio, при эмуляции программ в среде MPLAB следует учитывать, что задержки при работе с реальным микроконтроллером намного меньше. С помощью параметров, расположенных в диалоговом окне Simulator Settings на вкладке Break Options можно управлять моментами останова эмуляции, вклю- чая останов по сигналу от сторожевого таймера (WDT) и останов по переполне- нию стека. Работа в режиме отладки/эмуляции Команды, используемые в режиме отладки, находятся в меню Debugger. Как и в AVR Studio, в MPLAB используются точки прерывания, обозначаемые красным кружком с символом “В” слева от строки исходного кода, в которых они установ- лены. Для установки/удаления точки прерывания следует дважды щелкнуть мы- шью на сером поле слева от сроки или выполнить команду контекстного меню Set Breakpoint. Для отключения/активизации и удаления точек прерывания используется диа- логовое окно Breakpoints, которое открывается по команде меню Debugger ► Breakpoints (клавиша <F2>), а также команды Disable Breakpoint (отключение), Enable Breakpoint (активизация) и Remove Breakpoint (удаление) контекстного ме- ню. Для отключения, активизации или удаления всех расставленных точек преры- вания можно воспользоваться тем же диалоговым окном Breakpoints или команда- ми подменю Breakpoints контекстного меню. Для перехода в режим отладки используются команды меню Debugger: Run (клавиша <F9>) — выполнение программы в режиме реального времени; переход в режим отладки происходит, если встречается точ- ка прерывания; Animate — выполнение программы с периодическим обновлением окон отладчика; Step Into (клавиша <F7>) — выполняет текущую команду с заходом в подпрограммы (все окна обновляются); Step Over (клавиша <F8>) — выполняет текущую команду без захода в подпрограммы (все окна обновляются); Step Out — запускает программу и выполняет ее до тех пор, пока не встретится окончание текущей подпрограммы; если ход выполнения находится в области основной программы, то программа будет выполнять- ся до тех пор, пока не будет остановлена пользователем командой Halt или не встретит точку прерывания; Halt (клавиша <F5>) — принудительный останов работы программы. 1> в 0D
Программное обеспечение для микроконтроллеров PIC 103 Для сброса в исходное состояние используются команды подменю г™" Debugger ► Reset, а также кнопка Reset панели инструментов Debug (клавиша - <F6>). Еще одна возможность — выполнение программы до строки, в которой в дан- ный момент установлен курсор. Для этого используется команда контекстного меню Run to Cursor. Окна отладчика Вызов окон отладчика реализован с помощью команд меню View. Прежде все- го, интерес представляют окна Watch, File Registers и Special Function Registers. Ти- пичное для отладчиков окно Watch (рис. 2.40) позволяет просматривать значения переменных и содержимое регистров управления во время выполнения програм- мы. С помощью кнопки Add SFR и расположенного справа от нее раскрывающего- ся списка можно создать элемент для просмотра содержимого какого-либо реги- стра управления (в примере на рис. 2.40 — регистра данных порта D). Аналогич- ное назначение, но для переменных, имеет кнопка Add Symbol и расположенное справа поле. Для того чтобы изменить формат отображенной значения некоторого элемента просмотра, на нем следует щелкнуть правой кнопкой мыши, выбрать в контекстном меню команду Properties и задать требуемый формат на вкладке Watch Properties. Для удаления текущего элемента просмотра достаточно нажать клавишу <De- lete> или же воспользоваться командой Delete контекстного меню. Следующее окно отладчика, представляющее интерес, — File Registers, ото- бражающее содержимое памяти данных. Содержимое памяти можно просмотреть в виде шестнадцатеричного дампа (вкладка Hex) или же последовательного списка ячеек с отображением их содержимого в различных форматах (вкладка Symbolic) (рис. 2.41). Значения, измененные в процессе выполнения программы, выделяются в окне File Registers красным цветом. Окно File Registers позволяет редактировать расположенные в памяти пере- менные непосредственно или с помощью специального окна, которое открывается по команде контекстного меню Fill Registers (рис. 2.42). В окне Fill Registers можно указать начальный и конечный адрес области памяти, занимаемой переменной, в поле Data вести требуемые данные в шестнадцатеричной или десятичной форме (это определяется с помощью переключателей Data Radix) и нажать кнопку Write. Для выхода без выполнения записи используется кнопка Close.
104 Глава 2. Компиляторы и средства разработки м File Registers Address I 00 I 01 i 02 03 I 04 OS Об I 07 08 j 09 | OA OB ОС OD OE OF 0000 0010 00 05 00 00 00 00 00 00 00 00 00 64 00 1D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ямивиди d.... Hex [Ml File Registers Address 0001 [ Hex [ вТЦ 05 Decimal 1 Binary 00000101 0002 00 0 00000000 0003 00 0 00000000 0004 00 0 00000000 0005 00 0 00000000 0006 64 100 01100100 0007 ID 29 00011101 d "char~f symbol Name 1 JdJxI RETURN Hex || Symbolic Pause Sins ,ay msl$??32767 Рис. 2.41. Вкладки Hex и Symbolic окна File Registers Наконец, последнее окно отладчика, которое мы рассмотрим, — это Special Function Registers (рис. 2.43). Как понятно из его названия, оно используется для про- смотра и модификации содержимого регистров управле- ния различных устройств микроконтроллера (таймеры, порты ввода/вывода, последовательные интерфейсы и т.д.). К примеру, рассмотренная выше программа SOS. с изменяет содержимое регистров PORTD (адрес 0x0F83), LATD (адрес OxOF8C) и TRISD (адрес 0x0F95). Start Address: pxoooi Data: joxos Назначение и структура различных регистров управления мик- роконтроллеров PIC рассматривается в предыдущей главе. Значения, измененные в процессе выполнения про- граммы, выделяются в окне Special Function Registers красным цветом. Ml Special Function Registers ddress V I SFR Name 1 Hex | Decimal | Binary 0F83 PORTD I ff| • 255- 11111111 0F84 PORTE 00 0 00000000 □F89 LATA 00 0 00000000 0F8A LATE 00 0 00000000 0F8B LATC 00 0 00000000 0F8C LATD FF 255 11111111 □ F8D LATE 00 0 00000000 0F92 TRISA 7F 127 01111111 0F93 TRISB FF 255 11111111 0F94 TRISC FF 255 11111111 0F95 TRISD 00 0 00000000 UseDataAs ......‘ & Fill Value Sequence Start ; Randomize Data Г Retain Address Range / | Write ~~| Close |- Рис. 2.42. Диалоговое окно Fill Registers -.|Q|x| Char[Z Рис. 2.43. Окно Special Function Registers Так же как и в окне File Registers, в этом окне можно изменять содержимое ре- гистров прямо в процессе выполнения программы (в режиме отладки).
Программирование целевого устройства 105 Программирование целевого устройства Эмуляция эмуляцией, но любая программа, предназначенная для использова- ния в микроконтроллерах, конечно же, должна быть каким-то образом записана в память программ целевого устройства. Какое бы средство программирования для этого ни использовалось, процесс всегда выглядит одинаково: 1. Подсоединить микроконтроллер к программатору, который в свою очередь соединен с компьютером. 2. Перевести микроконтроллер в режим программирования. 3. Записать данные в память (Flash или EEPROM) с помощью специальных про- граммный средств. 4. Вывести микроконтроллер из режима программирования. Программаторы могут использоваться самые разнообразные, поэтому в дан- ной книге мы затронем только вопросы, имеющие отношение к третьему пункту описанного выше процесса. Вопросы, связанные с аппаратной частью процесса программирования, можно выяснить с помощью технических описаний микро- контроллеров и программаторов. Программирование устройства в среде A VR Studio 4 В среде AVR Studio 4 для записи программ в память микроконтроллера сле- дует выполнить команду меню Tools ► AVR Prog. В результате откроется диалого- вое окно, представленное на рис. 2.44. В этом окне следует ввести имя объектного файла в поле Hex file (или найти его, нажав кнопку Browse), а затем нажать кнопку Program в разделе Flash или EEPROM — в зависимости от типа памяти, в которую выполняется запись. По завершению процесса будет выдано сообщение. В том случае, если при попытке установить связь с программатором были об- наружены какие-нибудь проблемы (например, программатор некорректно под- ключен, нарушен кабель или порт занят другой программой), то на экране вместо окна AVRProg появится сообщение, представленное на рис. 2.45. Рис. 2.44. Диалоговое окно для записи программ в память микроконтроллера Рис. 2.45. Сообщение о том, что связь с программатором не установлена
106 Глава 2. Компиляторы и средства разработки Программирование устройств в среде CCS-PICC Процесс программирования микроконтроллеров PIC аналогичен описанному выше. При этом соответствующие средства доступны как в среде разработки CCS- PICC, так и в MPLAB. Напоминаем, что в среде CCS-PICC выбор формата объектного файла, полу- чаемого в результате компиляции программы, осуществляется в диалоговом окне File Formats (см. рис. 2.29), которое открывается по команде меню Options ► File formats. ICO Connection Settings COM port ffcOMI Baud Rate ' Программное средство для связи с программатором указывается в диалоговом окне Device Programmer/Debugger Options (см. рис. 2.33), которое открывается по команде меню Options ► Debugger/Programmer, в поле Device Programmer. В ком- плект поставки компилятора CCS-PICC входит специальная программа icd.exe (по умолчанию, устанавливается в папку \Program. Files\PICC), организующая передачу данных в программатор через последовательный порт компьютера (фор- мат объектного файла — восьмиразрядный .hex). Для запуска этого средства из среды разработки CCS-PICC следует выполнить ко- манду меню Tools ► Program Chip или нажать Fj- соответствующую кнопку панели инструмен- тов. В результате на экране появится диалоговое ок- но Start Connection (рис. 2.46), в котором следует выбрать последовательный порт и скорость переда- чи данных и нажать кнопку Connect. После того как связь установлена, откроется ок- но, в котором, как и в случае с AVR Studio, следует указать имя записываемого файла . hex, и затем на- жать кнопку Download to target. Рис. 2.46. Окно Start Connection Программирование устройств в среде MPLAB Для программирования микроконтроллеров в среде MPLAB используются команды из меню Programmer. Прежде всего, следует выбрать тип программатора с помощью подменю Programmer ► Select Programmer. Параметры соединения на- страиваются в диалоговом окне, которое открывается по команде меню Programmer ► Settings (внешний вид этого окна для различных программаторов отличается). Для установки соединения с программатором используется команда Programmer ► Connect или Programmer ► Enable Programmer (в зависимости от типа программатора), а для начала записи — команда Program.
Глава 3 Язык С и директивы препроцессора В этой главе кратко рассматривается синтаксис языка С, а также директивы препроцессора в том объеме, которого будет достаточно для изучения примеров из следующих двух глав. Даже если читатель уже знаком с представленными здесь вопросами, все же рекомендуем просмотреть эту главу, чтобы выяснить особенности, характерные для компиляторов WinAVR и CCS-PICC. Вводные понятия Прежде всего, рассмотрим такие вводные понятия как комментарии, ключе- вые слова, идентификаторы, литералы, операторы и знаки пунктуации. Комментарий — это некоторый поясняющий текст, который при компиляции не учитывается. Комментарии бывают многострочными (начинаются с символов /* и заканчиваются символами */) и однострочными (начинаются с символов //). В последнем случае комментарием считается вся часть строки, расположенная справа от символов / /. Примеры: /* Многострочный комментарий часто размещают в начале файла, где он содержит имя автора и описание программы */ #include <avr/io.h> //Подключаем заголовочный файл io.h Идентификатор — это последовательность букв, цифр и символов подчерки- вания которая не должна начинаться с цифры, используемая для именования различных программных элементов, наподобие переменных, констант, функций, типов и т.д. Регистр букв имеет значение. Ключевое слово — это зарезервированное слово четко определенного назна- чения. Ключевые слова не могут использоваться в качестве идентификаторов: asm, auto; bit, bool, break; case, char, const, continue; default, defined, do, double; else, enum, explicit, extern; false, float, for; goto; if, inline, int; long; register, return; short, signed, sizeof, static, struct, switch; true, typedef; union, unsigned; void; while.
108 Глава 3. Язык С и директивы препроцессора Литерал — постоянное значение некоторого типа, используемое в выражени- ях. Примеры числовых литералов: 10 — число 10 в десятичной форме; ОхА — число 10 в шестнадцатеричной форме (префикс Ох); 0Ы010 — число 10 в двоичной форме (префикс 0b); 012 — число 10 в восьмеричной форме (префикс 0); 10 .5 — число с плавающей точкой; 105е-1 — число 10,5 в экспоненциальной форме; 10U — беззнаковая константа (суффикс и); 10L — знаковая константа (суффикс L). Символьные литералы заключаются в одинарные кавычки, например, ' в', ' * '. Для обозначения непечатаемых и специальных символов в литералах исполь- зуются так называемые escape-последовательности: ' \а ' — звуковой сигнал; ' \Ь' — клавиша <Backspace>; ' \ f' — прогон листа; ' \п' — символ перевода строки; ' \г' — возврат каретки; ' \t' — горизонтальная табуляция; ' \ v' — вертикальная табуляция; ' \ 0 ' — нулевой символ; ' \ \ ' — обратная косая; ’ \ ' — апостроф. Кроме того, любой символ можно представить с помощью литерала по его ASCH-коду, например, литерал '\t ’ равнозначен '\х09' (в шестнадцатеричном представлении). Строковые литералы ограничиваются двойными кавычками, а в памяти хра- нятся как последовательности символов, заканчивающиеся нулевым символом ' \ 0'. Специальные символы внутри строки должны предваряться обратной косой (“\”). Примеры строковых литералов: " " — пустая строка: один символ ' \ 0 '; "В"—два символа: 'в' и '\0 '; "A\tB\n" — пять символов: ' А', табуляция, ' в', перевод строки, '\0 '. Оператор — это символ, указывающий компилятору, какие действия выпол- нить над операндами. Некоторые символы могут трактоваться по-разному в зави- симости от контекста. Например, знак может использоваться для изменения знака числа или в качестве оператора вычитания. Операторы, соединяющие опе- ранды, представляют собой выражения. Выражения могут быть заключены в круглые скобки и отделяются друг от друга символом точки с запятой (“;’’)•
Вводные понятия 109 Приоритетность выполнения операторов в выражениях языка С указана в табл. 3.1 (приоритет с меньшим номером уровня — выше). Таблица 3.1. Приоритетность выполнения операторов в выражениях языка С Уровень Операторы Категория Описание 1 0 Круглые скобки [] Элемент массива Доступ к данным Обращение к элементу структуры, напри- мер: portb . 1 — разряд 1 порта В Обращение к элементу структуры, опреде- ленной указателем, например: pStruct->x — элемент х структуры, на ко- торую указывает pStruct (постфиксы) Арифметические Операторы автоинкремента и автодекре- мента после того как выражение, в котором задействованы соответствующие операнды, вычислено. Примеры: а = Ь++; равнозначно а = b; Ь = Ь+1; а = Ь—; равнозначно а = b; Ь = Ь-1; 2 (префиксы) Операторы автоинкремента и автодекре- мента перед тем как выражение, в котором задействованы соответствующие операнды, будет вычислено. Примеры: а = ++Ь; равнозначно b = Ь+1; а = Ь; а = —Ь; равнозначно Ь = Ь-1; а = Ь; I Логические Логическое(унарное) отрицание Поразрядные Поразрядное отрицание & Доступ к данным Адрес 3 +, - (унарные) Арифметические Изменение знака операнда * (унарный) Доступ к данным Разыменование указателя 4 * (бинарный) Арифметические Умножение / Деление о о Остаток от деления 5 +, - (бинарные) Сложение и вычитание 6 << Поразрядные Поразрядный сдвиг влево >> Поразрядный сдвиг вправо 7 <><->= Сравнения Меньше, больше и т.д. 8 —= I — Равно, не равно 9 & Поразрядные Поразрядное "И" 10 Л Поразрядные Поразрядное "Исключающее ИЛИ" 11 I Поразрядные Поразрядное "ИЛИ" 12 && Логические Логическое "И" 13 11 Логические Логическое "ИЛИ" 14 Присваивание Возможны также сочетания оператора при- сваивания с арифметическими и поразряд- ными операторами, например: а += Ь; равнозначно а = а + Ь; а *= Ь+с; равнозначно а = а * (Ь + с) ; Объединенные по некоторому признаку последовательности выражений за- ключаются в фигурные скобки { }. Так, к примеру, обозначаются границы функ-
110 Глава 3. Язык С и директивы препроцессора ций, а также блоки выражений в циклических и условных конструкциях (см. ниже соответствующие разделы этой главы). Структура программы на С Структуру программы, написанной на языке С» изучим на основании примера SOS. с (см. листинг 2.2). //Директивы препроцессора ttinclude <18F458.h> #use delay(clock-20000000) #fuses HS,WDT //Объявления глобальных типов, переменных и констант //Функции Functionl { //Объявления локальных типов, переменных и констант //Операторы } FunctionN { //Объявления локальных тиг!ов, переменных и констант //Операторы } //Главная функция программы int main (void) { //Объявления локальных типов, переменных и констант //Операторы } Программы обычно начинаются с директив Препроцессора (начинаются с символа “#”), которые, по сути, не являются конструкциями языка С и обраба- тываются до фактической компиляции программы. Их смысл — подстановка не- которого кода в программу. Так, к примеру, очень часто используется директива #include, которая включает в файл с исходным кодом программы текст внешне- го заголовочного файла (с расширением . h). Заголовочные файлы содержат оп- ределения глобальных типов, констант» переменных и функций. Более подробно директивы препроцессора рассматриваются в конце этой главы.
Типы данных, переменные, константы 111 Типы данных, переменные, константы Тип данных определяет диапазон допустимых значений и пространство, от- водимое в памяти данных, для переменных, констант и результатов, возвращае- мых функциями. В различных компиляторах с языка С могут использоваться соб- ственные типы данных, однако все они базируются на стандартных типах, пере- численных в табл. 3.2. Таблица 3.2. Стандартные типы данных языка С Тип Название Размер, в битах Диапазон значений bit Бит 1 0, 1 char Символ 8 -128.. 127 int Целое число 16 -32768..32767 float Вещественное число 32 ±1,175-КГ38.. +3,402-1038 long int Длинное целое 32 -2147483648..2147483647 unsigned char Беззнаковый символ или логическое значение 8 0..255 TRUE, FALSE unsigned int Беззнаковое целое 16 0..65535 unsigned long int Беззнаковое длинное целое 32 0..4294967295 Рассмотрим некоторые определения подробнее, чтобы понять, каким образом получаются перечисленные в табл. 3.2 диапазоны допустимых значений. Бит — это базовая единица информации в вычислительной технике, озна- чающая одно из двух состояний: 0 или 1, высокий уровень сигнала или низкий уровень сигнала. К примеру, при подключении светодиодов к выводам микрокон- троллера для каждого такого вывода программист, опять таки, оперирует поняти- ем бита, поскольку в данном случае речь идет об двух уровнях напряжения, один из которых включает, а другой — отключает светодиод. В общем случае, говорят, что бит содержит логический “0” или логическую “1”. Следующая фундаментальная единицы информации — байт, состоящий из восьми битов. С его помощью можно представить 256 различных состояний (0..255), что иллюстрирует табл. 3.3. Таблица 3.3. Значения, которые можно получить с помощью одного байта Значение Двоичное представление Восьмеричное представление Шестнадцатеричное представление 0 оьоооооооо 0000 0x00 1 ObOOOOOOOl 0001 0x01 2 оьоооооою 0002 0x02 3 ObOOOOOOll 0003 0x03 4 оьоооооюо 0004 0x04 5 ObOOOOOlOl 0005 0x05 6 ObOOOOOllO 0006 0x06 7 ObOOOOOlll 0007 0x07 8 ObOOOOlOOO 0010 0x08 9 ObOOOOlOOl ООН 0x09 10 ObOOOOlOlO 0012 0х0А 11 ObOOOOlOll 0013 0x0В 12 ObOOOOllOO 0014 ОхОС 13 ObOOOOllOl 0015 0x0D 14 ObOOOOlllO 0016 0х0Е
112 Глава 3. Язык С и директивы препроцессора Таблица 3.3. Окончание Значение Двоичное представление Восьмеричное представление Шестнадцатеричное представление 15 obooooini 0017 0x0F 16 оьоооюооо 0020 0x10 254 0Ы1111110 0376 OxFE 255 0Ы1111111 0377 OxFF 7W. Полностью таблица представления чисел в различных системах счисления дана в приложе- нии Б. Правила преобразований из одной системы счисления в другую Схема преобразования из некоторой системы счисления в десятичную очень проста: каждый разряд умножаем на основание системы, возведенное в соответ- ствующую разряду степень (начиная с 0), и затем складываем полученные произ- ведения для всех разрядов. Пример для десятичного числа 100: ОЬОНООЮО = 0-27 + 1-26 + 1-25 + 0-24 + 0-23 + 1-22 + 0-21 + 0-2° = 0 + 64 + 32 + 0 + 0 + 4 + 0 + 0 = 100; 0144 = 1 -82 + 4-81 + 4-8° = 64+ 32+ 4= 100; 0x64 =6-16' +4-16° = 96+ 4= 100. Для преобразования из десятичного представления в другую систему счисле- ния число следует разделить на основание системы, запомнить остаток, затем ча- стное еще раз разделить на основание системы, опять запомнить остаток и т.д. до тех пор, пока не будет получено неделимое частное. Это частное является стар- шим разрядом полученного представления, а остальные разряды формируются из остатков, начиная от последнего к первому. Пример преобразования числа 100 в двоичную систему счисления: 100/2 50, остаток 0 50/2 = 25, остаток 0 25/2 — 12, остаток 1 12/2 — 6, остаток 0 6/2 — з, остаток 0 3/2 = 1, остаток 1 1 — на 2 не делится. Результат: 100 = ОЫЮОЮО. Пример преобразования числа 100 в восьмеричную систему счисления: 100/8 = 12, остаток 4 12/8 = 1, остаток 4 1 — на 8 не делится. Результат: 100 = 0144. Пример преобразования числа 100 в шестнадцатеричную систему счисления: 100/16 = 6, остаток 4 6 — на 16 не делится. Результат: 100 = 0x64.
Типы данных, переменные, константы 113 Тип char Байту данных в языке С соответствует тип char. Он получил свое название от английского слова “character”, поскольку Чаще всего используется в программах для хранения символов (то есть, их ASCII-кодов). Тем не менее, с его помощью можно обращаться и к небольшим целым числам в диапазоне от -128 до +127, а для типа unsigned char — от 0 до 255, поскольку в этом случае знаковый разряд не используется (использование слова unsigned имеет такой же смысл и в случае целочисленного типа int). ПРИМЕЧАНИЕ Во многих компиляторах с языка С вместо типа unsigned char используется его эквива- лент вида h/t-e или Byte. Пользовательские типы Язык С позволяет, кроме стандартных, объявлять собственные типы. Для это- го используется ключевое слово typedef, например: typedef unsigned char byte; //объявляем тип byte typedef unsigned int word; //объявляем тип word Другими словами, для объявления пользовательского типа используется кон- струкция вида typedef стандартный_тип идентификатор_пользовательского_типа; Так, в компиляторе WinAVR определены следующие пользовательские типы: typedef signed char int8_t — в заголовочном файле inttypes . h; typedef unsigned char uint8_t — в заголовочном файле int- types. h; typedef int intl6_t — в заголовочном файле inttypes . h; typedef unsigned int uintl6_t — в заголовочном файле inttypes . h; typedef long int32_t — в заголовочном файле inttypes . h; typedef unsigned long uint32_t — в заголовочном файле inttypes. h; typedef long long int64_t — в заголовочном файле inttypes . h; typedef unsigned long long uint64_t — в файле inttypes . h; typedef struct {int quot; int rem;} <±Lv_t; —В заголовочном файле stdlib. h (этот тип используется стандартной функцией div ()); typedef struct {long quot; long rem;} ldiv_t; —в заголовоч- ном файле stdlib. h (этоттип используется стандартной функцией ldiv() ). В компиляторе CCS-PICC также определены некоторые пользовательские це- лочисленные типы: | inti, short — определяют тип размером в один бит; 8 — 6-767
114 Глава 3. Язык С и директивы препроцессора int 8 — аналог стандартного типа char; int 16 — аналог стандартного типа int; int32 — аналог стандартного типа long int. Переменные Переменная — это именованная величина определенного типа, которая мо- жет изменяться в ходе выполнения программы. Для объявления переменных (т.е., выделения для них памяти) в программе на С используется следующая конструк- ция: тип_переменной идентификатор!, идентификатор2, Например: int i; //Объявление целочисленной переменной i char cl, с2; //Объявление символьных переменных cl и с2 ПРИМЕЧАНИЕ Пользовательский тип, используемый при объявлении переменной, должен быть объявлен выше в тексте программы. Для доступа к переменной в программе используется соответствующий иден- тификатор (обязательно после объявления переменной). Значения, присваиваемые переменной, должны соответствовать ей по типу (или правилам приведения ти- пов, рассматриваемым ниже). Примеры: i = 2; //Ошибка! Переменная I еще не объявлена int i; //Объявление целочисленной переменной i float f; //Объявление вещественной переменной f i = 2; //Переменной i присвоено значение 2 f = 3.3 //Переменной f присвоено значение 3,3 f = i; //Переменной f присвоено значение переменной i //(в данном случае будет выполнено автоматическое //приведение типов, т.е. f = 2.0) По области видимости переменные могут быть глобальными и локальными. К глобальным переменным имеют доступ все функции программы. Такие пере- менные объявляются в программе перед объявлением всех функций. К локаль- ным переменным имеет доступ только та функция, в которой они объявлены. Функции рассматриваются ниже в соответствующем разделе главы. Область видимости переменных Имена переменных обладают определенной областью видимости, которая подразумевает, что компилятор использует переменные в соответствии с тем, где они находятся. Имена переменных, объявленных внутри функции, имеют область видимости, ограниченную конкретной функцией. Например, в нескольких функ- циях можно объявить переменную int i, которая в каждой функции не будет иметь никакой связи с аналогичными переменными в других функциях. Точно так же и переменная, объявленная внутри блока (ограничен фигурными скобками {}), остается локальной по отношению к этому блоку.
Типы данных, переменные, константы 115 Глобальные переменные имеют область видимости, которая начинается от места их объявления и продолжается до конца программного файла. Для того что- бы глобальную переменную можно было использовать в других файлах, ее нужно объявить с помощью ключевого слова extern: extern Объявленную таким образом переменную, прежде, чем ее использовать, сле- дует обязательно инициализировать во внешнем файле некоторым значением. ВНИМАНИЕ! Использование переменных, объявленных с помощью ключевого слова extern, часто при- водит к ошибкам в программах, поэтому применяйте их только тогда, когда нет другой аль- тернативы, и будьте при этом очень внимательны. Константы Константа — это именованная величина определенного типа, которая, в от- личие от переменной, не может изменяться в ходе выполнения программы, а име- ет конкретное значение, определенное в момент объявления. Для объявления кон- стант в программе на С используется следующая конструкция: const тип_константы идентификатор = значение; Например: const int i = 10; //Объявление целочисленной константы i ПРИМЕЧАНИЕ Пользовательский тип, используемый при объявлении константы, должен быть объявлен выше в тексте программы. Величина, объявленная как константа, будет размещена компилятором в па- мяти программ, а не в ограниченной области переменных в RAM. Для доступа к константе используется ее идентификатор. Примеры: с = 'А'; //Ошибка! Константа с еще не объявлена int i; //Объявление целочисленной переменной i const с = 'А'; //Объявление константы с i = 2; //Переменной i присвоено значение 2 с = i; //Ошибка! Попытка присвоить значение константе i = с; //Переменной i присвоено значение константы с. //В данном случае будет выполнено автоматическое //приведение типов, т.е. i = 65 (ASCII-код символа 'А') ПРИМЕЧАНИЕ Как было отмечено в главе 1, в микроконтроллерах AVR и PIC используется раздельная память для данных (SRAM) и программ (Flash), а также память типа EEPROM. Переменные, если не указано иного, по умолчанию размещаются в памяти SRAM, но, встречая слово, const компилятор относит соответствующие значения к постоянной флэш-памяти. Кроме того, компиляторы WinAVR и CCS-PICC позволяют записывать константы в память EEPROM и считывать их с помощью соответствующих функций (см. приложение Д). 8*
116 Глава 3. Язык С и директивы препроцессора Перечислимые типы Перечислимый тип — это объявление списка целочисленных констант, ко- торые можно явно не инициализировать (в этом случае компилятор считает, что первая константа в списке принимает значение 0, вторая — 1 и т.д )- Для подобно- го объявления используется ключевое слово enum: int п; enum (zero, one, two); //zero = 0; one = 1; two = 2 n = one; //n = 1 Если требуется изменить начальное значение для списка констант, то можно указать его явно при объявлении, например: enum (three = 3, four, five); //three = 3; four = 4; five = 5 Приведение типов Приведение типов — это принудительное преобразование значения одного типа к другому, совместимому с исходным. Это важно при выполнении арифме- тических операций, когда полученные значения могут выходить за допустимые пределы. Приведение типов бывает явным и неявным. Неявное приведение типов ис- пользуется в операторах присваивания, когда компилятор сам выполняет необхо- димые преобразования без участия программиста. Примеры подобного приведе- ния уже рассматривались выше в подразделах, посвященных переменным и кон- стантам. Для явного приведения типа некоторой переменной перед ней следует указать в круглых скобках имя нового типа, например: int X; int Y = 200; char С = 30; X = (int)С * 10 + Y; //Переменная С приведена к типу int Если бы в этом примере не было выполнено явное приведение типов, то ком- пилятор предположил бы, что выражение С * 10 — это восьмиразрядное умно- жение (разрядности типа char) и вместо корректного значения 300 (0х12С) в стек было бы помещено урезанное значение 4 4 (0х2С). Таким образом, в результате вычисления выражения С * 10 + Y переменной X было бы присвоено значение 64 0, а не корректное 32 00. В результате приведения типа переменная с распозна- ется компилятором как 16-тиразрядная, и описанной выше ошибки не возникает. Оператор sizeof Оператор sizeof применяется для вычисления размера области памяти (в байтах), отводимой под некоторую переменную, результат выражения или тип. Например: int а; float f; а = sizeof(int); //а = 2 а = sizeof(f); //а = 4
Функции 117 f = 3.3; а = sizeof(f + а); //а = 4, поскольку тип результата - float Функции Функция представляет собой “контейнер”, в котором выполняется некоторый фрагмент программного кода. Использование функций упрощает написание и от- ладку программ, поскольку в них удобно размещать повторяющиеся группы опе- раторов. Любая программа на языке С содержит главную функцию под названием main (). Эта функция при запуске программы выполняется первой. > Стандартные, а также характерные для компиляторов WinAVR и CCS-PICC библиотечные функции перечислены в приложении Д. Пользовательские функции определяются после директив препроцессора и глобальных объявлений типов, переменных и констант в файле с исходным кодом или в заголовочном файле. При этом используется следующий синтаксис: Тип_возвращаемого_значения Имя_функции(Список_параметров) { //Тело функции } ПРИМЕЧАНИЕ Предварительное объявление функции может отсутствовать. В этом случае она доступна только внутри того файла, в котором определена. В качестве типа возвращаемого значения может использоваться ключевое слово void. Это означает, что функция или не возвращает никакого значения (в некоторых языка программирования такие функции называют процедурами). Функцию вызывают по ее имени с указанием в круглых скобках перечня пе- редаваемых параметров (если их нет, то в скобках ничего не указывается), напри- мер: void Functionl(int n, char c) { } int Function2() { } int main() { int x; char y; Functionl(x, y) ; x = Function2() ;
118 Глава 3. Язык С и директивы препроцессора Параметры — это идентификаторы, которые могут использоваться внутри функции. Вместо них подставляются соответствующие значения, указанные при вызове функции (если в функцию передается более одного параметра, то они от- деляются друг от друга запятыми как при объявлении, так и при вызове). Значения, переданные в функцию, фактически не изменяются, а просто копи- руются в параметры, который в этом смысле выполняют роль локальных пере- менных. При этом следует следить за тем, чтобы тип передаваемых значений со- ответствовал типу параметров, объявленных в заголовке функции. Возвращаемые значения Если функция предназначена для возврата значения некоторого типа, то для этого в ее теле используют ключевое слово return, после которого (через пробел) указывают возвращаемое значение. При этом все операторы после слова return игнорируются, и происходит возврат в вызывающую функцию. Пример: int powerS(int n) { return n*n*n; } vold main() { int x; x = power3(2); //x = 8 } Слово return может также использоваться без указания возвращаемого вы- ражения. В этом случае оно просто означает выход из функции. Прототипы функций В обычном варианте функции используются только после их определения, однако бывают случаи, когда функции вызывают друг друга, и организовать их “правильное” определение невозможно. Обойти подобную проблему позволяют прототипы функций, которые представляют собой объявление до определения. Такое объявление представляет собой только заголовок функции, причем в списке параметров указывают только типы, без идентификаторов, например: int fl(int); void f2(int, int); int fl(int x) { } void f2(int a, int b) {
Функции 119 Прототипы функций часто используются в заголовочных файлах, включае- мых в текст программы с помощью директивы препроцессора ^include. Классы памяти при объявлении локальных переменных Локальные переменные могут быть объявлены внутри функций как принад- лежащие к одному из трех классов памяти: auto (значение по умолчанию, можно явно не указывать) — при объявле- нии переменная не инициализируется никаким значением (значение — те- кущее содержимое области памяти, отведенной под переменную); при вы- ходе из функции переменная удаляется из памяти; static — статическая переменная доступна только в пределах функции, хотя память для нее выделяется в пространстве глобальных переменных; при первом обращении к функции инициализируется нулевым значением, и после выхода из функции из памяти не удаляется (таким образом, при последующих обращениях к функции в ней содержится старое значение); register — аналог автоматической локальной переменной за тем исклю- чением, что компилятор попытается выделить для нее не область памяти данных, а рабочий регистр микроконтроллера, что значительно ускоряет обращение к значению переменной. Пример использования статической переменной: int plus5() { static int x; return x + 5 ; } void main() { int y; у = plus5( ) ; //y = 5 у = plus5(); //у = 10 у = plus5(); //у = 15 Рекурсия Рекурсия — это вызов функцией самой себя. Эта возможность бывает трудна в понимании, и потому не удивительно, что эксперты по языку С так любят рекур- сивные функции. Пример рекурсивного вызова: void fl(int n) { int x; fl(x);
120 Глава 3. Язык С и директивы препроцессора Несмотря на сложность восприятия, рекурсия довольно часто используется в стандартных библиотечных функциях, а также во многих алгоритмах сортиров- ки. Тем не менее, при программировании микроконтроллеров использование ре- курсии, как правило, чревато проблемами из-за ограниченного объема оператив- ной памяти. Дело в том, что при каждом вызове рекурсивной функции часть памя- ти расходуется на сохранение данных, помещаемых в стек. Эти данные хранятся там до тех пор, пока не будет выполнен возврат из функции. Таким образом, когда рекурсивная функция снова и снова вызывает саму себя, в стеке остается все меньше и меньше свободной памяти. Можно сказать, что в подавляющем большинстве случаев использование ре- курсии при программировании микроконтроллеров — это надежный способ быст- рого и непредсказуемого заполнения стека. Это очень часто приводит к возникно- вению ошибочного состояния, называемого переполнением стека. Область стека обычно размещается в верхней части памяти и растет “вниз”, тогда как область переменных размещается в нижней части памяти и растет “вверх”. Поэтому если объем данных, помещаемых в стек, превысит размер области стека, эти данные могут достигнуть области переменных и затереть собой ее значения. При этом ошибку переполнения стека не всегда легко выявить, поскольку она может прояв- ляться лишь периодически. ПРИМЕЧАНИЕ Подобная проблема может также возникнуть при многократном вложении функций, то есть когда функция fl вызывает функцию t2, которая вызывает функцию £3, которая вызывает функцию f4 и т.д. Структуры Структура — это особый тип данных, состоящий из нескольких разнотипных переменных (полей). В общем случае объявление структуры имеет следующий вид: struct имя_структуры { тип поле_1; тип поле_Ь1; } ; Как и любой другой тип, структуру можно в дальнейшем использовать для объявления переменных, например: struct MyStructure { //Объявление структуры MyStructure int Fieldl; char Field2; float Field3; }; //Объявление переменных YourStruct и OurStruct типа MyStructure struct MyStructure YourStruct, OurStruct; Допускается инициализация полей непосредственно при объявлении пере- менных-структур с помощью перечня значений в фигурных скобках, например:
Указатели и адреса переменных 121 struct DATE { int Day; int Month; int Year; } struct DATE MyBirthday = {7, 8, 1974}; Для доступа к полям структуры в программе используют запись вида имя_структуры. поле. То есть, в представленном выше примере структуры DATE для инициализации полей можно было воспользоваться следующими оператора- ми: MyBirthday.Day = 7; MyBirthday.Month = 8; MyBirthday.Year = 1974 ; Структуры, в свою очередь, могут быть полями других структур, например: struct ME { char MyName[30]; //Строка длиной 30 символов - имя struct DATE MyBirthday; //Структура, хранящая день рождения } struct ME MyData; MyData.MyName = "John Smith"; MyData.MyBirthday.Day = 7; MyData.MyBirthday.Month = 8; MyData.MyBirthday.Year = 1974; Структуры могут выступать в качестве параметров функций, а также возвра- щаемого результата. Ниже представлен пример функции, возвращающей структу- ру типа DATE: struct DATE Januaryl(int CurYear) { struct DATE JanOl; JanOl.Day = 1; JanOl.Month = 1; JanOl.Year = CurYear; return JanOl; } struct DATE BeginOfTheYear; BeginOfTheYear = Januaryl(2006) ; Указатели и адреса переменных Указатель — это переменная, содержащая адрес некоторого элемента данных (переменной, константы, функции, структуры). В языке С указатели тесно связаны с обработкой массивов и строк, которые будут рассмотрены в следующем разделе. Для объявления переменной как указателя используется оператор *: int *р; //р - указатель на целое число
122 Глава 3. Язык С и директивы препроцессора Для присвоения адреса некоторой переменной указателю используется опера- тор &, например: char *р; //указатель на символ char с; //символьная переменная с = ' А' ; р = &с; // р содержит адрес переменной с (указывает на 'А') Для того чтобы извлечь значение переменной, на которую указывает указа- тель, используется оператор разыменования *. char *р; char с, Ь; с = ' А' ; р = & с ; b = *р; //Теперь b = 'А' Аналогичным образом, этот оператор можно использовать и для присвоения некоторого значения переменной, на которую указывает указатель: char *р; char с, Ь; b = ' А' ; Р = & с ; *р = Ь; //Теперь с = 'А' Применительно к программированию микроконтроллеров, указатели можно использовать, к примеру, для записи данных в порт ввода/вывода. Предположим, регистр данных порта расположен в памяти по адресу 0x16. В этом случае, для записи в него значения OxFF можно воспользоваться следующим фрагментом программного кода: unsigned char *thePort; thePort = 0x16; *thePort = OxFF; Передача в функции параметров по ссылке С помощью указателей, используемых в качестве параметров функций, можно организовать возврат более одного значения. Рассмотрим следующий пример: int SuinAndDiv (int *а, int *b) { int bufA, bufB; bufA = *a; bufB = *b; *a = bufA / bufB; //Указатель ссылается на результат //целочисленного деления без остатка *b = bufA % bufB; //Указатель ссылается на остаток от //целочисленного деления return bufA + bufB; //Функция возвращает сумму } int vl, v2, sum;
Указатели и адреса переменных 123 vl = 10; v2 = 3; sum = SumAndDiv(&vl, &v2); //sum = 13; vl = 3; v2 = 1 В функции SumAndDiv вначале сохраняются в буферных переменных значе- ния, на которые указывают указатели а и Ь. Затем в переменную, на которую ука- зывает указатель а записывается результат деления переданных в функцию значе- ний без остатка, а в переменную, на которую указывается указатель Ь — остаток от такого деления. Поскольку с помощью указателей мы напрямую обращались к ячейкам памяти, а не к переменным, то после выхода из функции содержимое этих ячеек остается неизменным. При вызове функции SumAndDiv в нее переда- ются адреса переменных vl и v2, которым в теле функции соответствуют указате- ли а и ь. Указатели на структуры На структуры можно создавать указатели точно так же, как и для любого дру- гого типа данных. Пример применения такого указателя: struct DATE { int Day; int Month; int Year; } struct DATE MyBirthday, *dateP; dateP = &MyBirthday; //dateP указывает на структуру MyBirthday В таком случае для доступа к полям структуры через указатель используется оператор ->: dateP->Day = 7; dateP->Month = 8; dateP->Year = 1974 ; Можно также использовать и разыменование указателя: ( *dateP) .Day = 7; (*dateP).Month = 8; (*dateP).Year = 1974; ПРИМЕЧАНИЕ В последнем примере операция *dateP заключена в скобки, поскольку оператор разыме- нования * имеет меньший приоритет по сравнению с оператором доступа к полю структуры . (см. табл. 3.1). Указатели также могут быть полями структуры. Это часто используется для создания в памяти цепочек однотипных структур, когда каждая предыдущая ука- зывает на следующую: struct DATE { int Day;
124 Глава 3. Язык С и директивы препроцессора int Month; int Year; struct DATE *next_date; //указатель на другую структуру //того же типа } struct DATE datel, date2; datel.next_date = &date2; Теперь, к примеру, в оператору datel. next_date->Year соответствует дос- туп к полю Year структуры date2 (то есть, это эквивалентно записи date2 . Year). Массивы и строки Массив — это тип данных, который используется для представления после- довательности однотипных значений. Массивы объявляются подобно обычным переменным, с указанием в квадратных скобках размерности (количества элемен- тов в массиве): int digits[10]; //Массив из десяти элементов типа int char str[10]; //Массив из десяти символов (строка) Доступ к элементам массива реализуется с помощью индекса (порядкового номера элемента, начиная с 0): digits[0] = 0; digits [ 1] = 1; s t г [ 0J = ' A' ; str[1] = 'В'; ПРИМЕЧАНИЕ По сути, имя массива — это указатель на его первый элемент. Так в последнем примере, оператор str [0] = ' А’; можно было бы заменить оператором *str = ’А';, а оператор str[l] = 'воператором * (str + 1) = 'В1;. Зачастую гораздо удобнее инициализировать массив непосредственно при его объявлении, например: int digits[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; char str[10] - {’T', 'h', 'e', ' 'i', 'n', 'e'}; int n; char.c; n = digits[2]; //n = 2 c = str[1]; //c = 'h' ВНИМАНИЕ! При разработке программ следует учитывать, что компилятор не всегда может отследить выход индекса за пределы массива. Другими словами, если бы в представленном выше примере мы использовали оператор с = str [п+10], то на этапе компиляции ошибки не возникнет, но в ходе выполнения программы переменной с будет присвоено значение “13- го”, несуществующего элемента массива, то есть извлечено содержимое области памяти, адрес которой на 12 элементов смещен относительно начального адреса массива.
Массивы и строки 125 Строки Строка в С — это массив типа char. Выше был рассмотрен пример объявле- ния такого массива, соответствующего строке “The line”. Это объявление можно было бы выполнить и с помощью строкового литерала: char str[10] = "The line"; Однако в таком случае следует помнить, что компилятор неявно завершает строковые литералы символом ' \0и, таким образом, реальная длина строки больше на 1. Это следует учитывать, чтобы при инициализации массива не выйти за его пределы. При инициализации строк размерность массива можно явно не указывать: char str[] = "The line"; В этом случае компилятор определит размерность самостоятельно. (Это пра- вило применимо и к инициализации любых других массивов.) ПРИМЕЧАНИЕ Для того чтобы строковые константы размещались не в памяти SRAM, а в флэш-памяти микроконтроллера, их следует объявлять с использованием слова const: const char str[). Многомерные массивы Язык С допускает использование многомерных массивов, то есть массивов, элементами которых являются массивы. Примеры объявления таких массивов: int а2[10] [2]; //Двухмерный массив 10x2 int а3[3] [2] [5]; //Трехмерный массив 3x2x5 Фактически, все элементы многомерного массива хранятся в памяти последо- вательно, поэтому представленные ниже примеры инициализации аналогичны: int а[2] [3] = { {1, 2, 3], {4, 5, 6} }; и int а [ 2 ] [ 3 ] = { 1, 2, 3, 4, 5, 6 } ; Для доступа к элементам многомерного массива используются индексы по каждой из размерностей или операции разыменования указателя. Например, что- бы извлечь в некоторую переменную п цифру 4 из представленного выше массива а, можно воспользоваться одним из двух вариантов: п = а[1][0]; //Извлекаем элемент из "строки" 1 (вторая), //"столбца" 0 (первый), то есть - цифру 4 п = *(а + 3); //а - указатель на первый элемент массива, //следовательно а + 3 - указатель на 4-й элемент
126 Глава 3. Язык С и директивы препроцессора Операторы ветвления Операторы ветвления используются для выполнения того или иного блока ко- да в зависимости от некоторого условия. К таким операторам в языке С относятся if-else и switch-case. Оператор if-else В простейшем случае оператор if-else имеет следующую структуру: if (условное_выражение) блок_кода_1; else блок_кода__2; Если условное выражение истинно, то выполняется блок кода 1, в противном случае — блок кода 2. При этом в качестве блока кода 2 допускается использовать последовательность операторов else-if: if (выражение!) блок_кода_1; else if (выражение2) блок_кода_2; else if (выражениеЗ) блок_кода_3; ... else блок_кода_М; В данном случае каждое выражение будет вычисляться поочередно до тех пор, пока не будет найдено выражение, давшее истинный результат. Если же ре- зультаты вычислений всех выражений окажутся ложными, то будет выполнен блок кода N. В тех случаях, когда при ложных результатах всех условных выражений не требуется выполнять никаких операторов, можно опустить завершающий блок кода вместе с последней ветвью else. Например: i f(а == 1) b = а * 3; else if(а == 2) Ь = а + 10; else if(а == 3) Ь = 0; Если выражение в первой строке будет истинным, остальные операторы будут пропущены, в противном случае начнется последовательная проверка выражений, указанных в следующих строках, до тех пор, пока не будет найдено выражение, дающее ненулевой результат, или до тех пор, пока не будет проверено последнее выражение. Если ни одно из выражений не будет истинным, никакие действия с переменной b выполнены не будут. Условные выражения Язык С позволяет вместо оператора if-else использовать условные выра- жения. Так, конструкцию вида if (условие) блок_кода_1; else блок_кода 2; можно заменить следующим условным выражением: условие ? блок_кода_1 : блок кода 2; Например: (а == 1) ? b = а*3 : b = 0; //Если а=1, то Ь=а*3, иначе Ь=0
Циклические конструкции 127 Оператор switch-case В оператора if-else можно использовать только выражения, которые сво- дятся к значению true или false. В тех случаях, когда необходимо применять выражения, дающие произвольный числовой результат, удобнее воспользоваться оператором switch-case. Этот оператор позволяет с помощью некоторой пере- менной выбирать одно из выражений, соответствующее заданной константе. Его синтаксис: switch (выражение) { case константа-выражение! case константа-выражение2 case константа-выражениеЗ default: блок кода блок_кода блок__кода блок__кода Продемонстрируем использование оператора switch-case на примере рас- смотренного выше фрагмента, реализованного с помощью оператора if-else: switch (а) { case 1 : b case 2 : b case 3 : b default: a * 3; break; a + 10; break; 0; break; Оператор break приводит к немедленному выходу из блока switch. Если по каким-то причинам необходимо продолжить проверку соответствия выражения выбора тем константам, которые указаны в ветвях case, следует убрать операто- ры break. Ко всему прочему, ветви case можно каскадировать. Такой прием особенно удобен в тех случаях, когда нужно, например, проверить введенный символ, не заботясь о том, представляет ли он прописную букву или строчную, или когда нужно получить одну и ту же реакцию на несколько разных чисел. Рассмотрим это на примере фрагмента некоторой абстрактной программы: switch (input) { case 'а' : case 'А' : DoA(); break; case 'b' : case 'B' : DoB(); break; case 'O' : case '1' : case '2' : case '3' : Do0123(); break; case '4' : case '5' : case '6' : case '7' : D04567(); break; default : DoDefault(); break; } Циклические конструкции Циклические конструкции применяют для повторения некоторого блока кода на основании условия цикла. В языке С используются циклические конструкции while, for и do-while.
128 Глава 3. Язык С и директивы препроцессора Конструкция while Цикл while имеет следующий синтаксис. while (условное_выражение) { // Выполнение тела цикла, если выражение истинно } Другими словами, циклы while имеет смысл использовать в тех случаях, ко- гда соответствующий оператор или блок операторов необходимо выполнять до тех пор, пока условное выражение истинно. Пример формирования строки, со- стоящей из нечетных цифр: int с, i; const char str [ ] = "0123456789"; char OddNums[5]; //Строка для хранения нечетных цифр с = 0; //Счетчик циклов' i = 0; //Индекс массива OddNums while (с < 10) //До тех пор, пока с меньше 10,... { //Если остаток от деления с на 2 = 1, то записываем в i-ю //позицию массива OddNums с-й элемент строки str, после чего //значение i автоматически инкрементируется if ((с % 2) == 1) OddNums[i++] = str[с]; C++; //с = с + 1“ } Конструкция for Цикл for имеет следующий синтаксис. for (выражение!; выражение2; выражениеЗ) { // Выполнение тела цикла } Выражение! выполняется только один раз при входе в цикл, и обычно пред- ставляет собой оператор присваивания некоторого начального значения счетчику цикла. Выражение2 — это условное выражение, определяющее момент выхода из цикла (цикл выполняется до тех пор, пока оно равно TRUE или 1). ВыражениеЗ — еще один оператор присваивания, в котором обычно изменяется счетчик цикла или некоторая переменная, влияющая на выполнение условия в выражении2. Вы- ражения могут быть представлены любыми операторами, включая пустые (то есть, вместо выражения можно поставить только символ точки с запятой). Циклы while и for в большинстве случаев взаимозаменяемы. Так, представ- ленный выше пример для цикла while, можно переписать в следующем виде: int с, i; const char str[] = "0123456789"; char OddNums[5]; //Строка для хранения нечетных цифр i = 0; //Индекс массива OddNums
Циклические конструкции 129 for (с =0; с < 10; с++) //До тех пор, пока с меньше 10,... if ((с % 2) == 1) OddNums[i++] = str[c]; В одних ситуациях удобнее применять циклы for, в других — while. Досто- инством циклов for является более наглядная инициализация и организация из- менения счетчика цикла. С другой стороны, циклы while более гибкие и обеспе- чивают больше возможностей для реализации нестандартных программных реше- ний при организации повторяющихся вычислений. Конструкция do-while Кроме циклов while и for, в которых вначале выполняется проверка истин- ности условия цикла, и только потом управление передается блоку операторов цикла, в языке С имеется также конструкция do-while. Она отличается от первых двух тем, что в ней вначале выполняется блок операторов, и только потом прове- ряется выполнение условия. Другими словами, цикл do-while всегда выполняет- ся как минимум один раз, вне зависимости от условия цикла. Цикл do-while имеет следующий синтаксис. do { // Выполнение блока операторов цикла } while (условное_выражение) ; 'Организация бесконечных циклов Для организации бесконечного цикла в качестве условного выражения в кон- струкции while или do-while можно просто указать значение true илй 1: while(1) блок_операторов; В случае циклов for это будет выглядеть следующим образом: for (;;) блок__операторов; Операторы break и continue Если в теле любого цикла встречается оператор break, управление тут же пе- редается на оператор, следующий за оператором цикла, вне зависимости от ис- тинности или неистинности условного выражения. При этом во вложенных цик- лах выход осуществляется не на самый верхний уровень вложенности, а лишь на один уровень вверх. При выполнении оператора continue все находящиеся после него операторы блока пропускаются, а управление передается в начало цикла для следующей ите- рации. На практике операторы continue используются гораздо реже, чем опера- торы break, однако в сложных циклах, требующих принятия решений на основа- нии многих факторов, использование операторов continue может быть весьма удобным. 9 — 6-767
130 Глава 3. Язык С и директивы препроцессора Стандартные функции ввода/вывода Стандартные функции ввода/вывода позволяют обмениваться информацией с выполняемой программой, что, к примеру, очень удобно в процессе отладки. Все они построены на основе функций посимвольного ввода/вывода, которые легко приспособить к аппаратным требованиям системы. Если при обычном примене- нии языка С эти функции используются для ввода данных с клавиатуры и вывода на экран или принтер, то применительно к микроконтроллерам AVR и PIC речь идет об обмене данными через приемопередатчик UART/USART (по умолчанию) или последовательный порт. Таким образом, прежде, чем начать ввод/вывод в программе должны быть выполнены соответствующие настройки (в частности, в компиляторе CCS-PICC для этой цели используется директива препроцессора #use, о чем речь пойдет чуть позже). >Все стандартные функции ввода-вывода компиляторов WinAVR и CCS-PICC перечислены в приложении Д. Ввод/вывод символов с помощью функций getchar () и putchar() Простейшими функциями ввода/вывода в языке С являются getchar () и putchar (), которые предназначены для посимвольного обмена данными (обе объявлены в стандартном заголовочном файле stdio.h). Функция getchar () возвращает символ, принятый от UART/USART или по последовательному ин- терфейсу, а функция putchar (), наоборот, выводит символ. Все остальные стан- дартные функции ввода/вывода базируются на них. Функции де с с. ла г (• и putchar () в компиляторах для микроконтроллеров реализуют ожи- дание готовности именно приемопередатчика UART/USART для передачи/приема символа. В том случае, если требуется организовать обмен данными по другому интерфейсу (напри- мер, по SPI), необходимо разработать собственные функции аналогичного содержания. Рассмотрим пример простой программы, выводящей в бесконечном цикле че- рез приемопередатчик UART/USART символы “1”, “2”, “3”, “4” и “5”. Исходный код программы putchar. с для микроконтроллеров AVR представлен в листин- ге 3.1. Пример — для устройства AT90S8515. Листинг 3.1. Программа putchar. с для микроконтроллеров AVR #include <avr/io.h> #include <stdio.h> int main (void) { const char str[] = "12345"; int i; UBRR = 25; //Скорость обмена через UART - 9600 бод //(см. табл. 1.17) UCR = 0x18; //Устанавливаем разряды TXEN и RXEN для //активизации UART в режиме ввода/вывода через
Стандартные функции ввода/вывода 131 Листинг 3.1. Окончание //выводы 0 и 1 порта D. //Разряд CHR9=0 - передача 8 бит данных 1=0; //Индекс строки символов while (1) //Бесконечный цикл { if (i > 4) i = 0; //Проверка выхода за пределы массива putchar(stг[i]); //Выводим i-й символ через UART } } Файлы putchar. с и makefile есть также на прилагаемом к книге компакт-диске в папке Projects\AVR\Putchar. Для микроконтроллеров PIC этот же пример будет выглядеть несколько иначе (листинг 3.2). В компиляторе CCS-PICC для настройки частоты системной син- хронизации и параметров обмена данными через UART используются директива препроцессора #use. 'Ж*' Более подробно директива #use рассматривается в конце этой главы. Листинг 3.2. Программа putchar. с для микроконтроллеров PIC #include <18F458.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232 (baud=9600, parity=N, xmit=PIN__C6, rcv=PIN_C7, stream=RS232, bits=8 void main() { const char str[] = "12345"; int i; i = 0; while (1) { if (i > 4) i = 0; putchar(str[i]); } <^а^Л Pu^c^ar • c Для компилятора CCS-PICC есть также на прилагаемом к книге компакт- диске в папке Projects\PIC\Putchar. Функции вывода строк puts () и printf() Функции puts () и printf() используют функцию putchar () для вывода посимвольно строки в порт RS232, назначенным последним. Предположим, у нас 9*
132 Глава 3. Язык С и директивы препроцессора есть определение строки, предназначенной для вывода через последовательный интерфейс: char s[6] = "12345"; Вызов функции puts () для вывода этой строки выглядит просто как puts(s);. Функция printf () несколько сложнее, поскольку позволяет применить фор- матированный вывод. Она имеет следующий синтаксис вызова: printf("Строка, в которую подставляются переменные ", переменная!, переменная2, .... ); В выводимой строке обычно используются спецификации форматирования значений переменных, переданных в качестве параметров. Каждая такая специфи- кация начинается со знака “%”, после которого следуют обозначения параметров форматирования: с — вывод параметра в виде символа ASCII; d — вывод параметра в виде целого числа; е — вывод параметра в экспоненциальном формате; f — вывод параметра в виде вещественного числа; id — вывод параметра в виде длинного целого со знаком; lu — вывод параметра в виде беззнакового длинного целого; Lx —вывод параметра в виде беззнакового длинного целого в шестнадца- теричном представлении, используя нижний регистр символов; LX —вывод параметра в виде беззнакового длинного целого в шестнадца- теричном представлении, используя верхний регистр символов; s — вывод параметра в виде строки; и — вывод параметра в виде беззнакового целого; х —вывод параметра в виде беззнакового целого в шестнадцатеричном представлении, используя нижний регистр символов; х —вывод параметра в виде беззнакового целого в шестнадцатеричном представлении, используя верхний регистр символов; % — вывод знака процента. Количество спецификаций должно совпадать с количеством переменных. При этом первая встретившаяся спецификация соответствует первой переменной в списке, вторая — второй и т.д. В случае вывода числовых значений сразу же после знака “%” может быть указано количество символов и формат для отображения чисел, например: 8 — восемь знаков; 0 8 — восемь знаков с дополнением ведущими нулями; 8.2 — восемь знаков, два знака после десятичной точки. Примеры использования спецификаций форматирования: int п 123;
Стандартные функции ввода/вывода 133 char с = '$'; float f = 23.5; char str[] = "Hello"; printf("n = %d, str = %s", n, str); //n = 123, str = Hello printf("n = %d : 0x%04X", n, n); //n = 123:0x007B printf("Salary = %c%8.2f", c, f); //Salary = $23.50 Функции ввода строк gets () и scanf () Функции gets () и scanf() выполняют действия, обратный функциям puts () и printf (): считывают посимвольно строку через назначенный послед- ним последовательный интерфейс. Рассмотрим пример использования функций gets () и puts () для микрокон- троллеров PIC (листинг 3.3). Листинг 3.3. Программа gets. с для микроконтроллеров PIC ttinclude <18F458.h> # fuses HS, NOWDT # use delay(clock=10000000) # use rs232(baud=9600, xmit=PIN_C6, rcv=PIN__C7) char si [12]; //Буфер, в который будет помещена принятая строка void main() { while (1) { gets (si); //Считываем строку из стандартного входного потока puts (si); //Выводим строку в стандартный выходной поток } } ь . тЦ Файл gets . с для компилятора CCS-PICC есть также на прилагаемом к книге компакт-диске в папке Projects\PIC\Gets. Функция scanf () считывает из входного потока значения в формате, опреде- ленном в первом параметре, и сохраняет их в переменных, адреса которых пере- даны ей в качестве последующих параметров. Спецификации форматирования для этой функции такие же, как и для printf (). Обычно функция scanf () использу- ется в паре с printf (): int х, у; puts("Enter two integers: "); scanf("%d,%x\n", &x, &y) ; //Считывает два числа, введенные //через запятую: одно - в десятичной, //а другое - в шестнадцатеричной форме printf("х = %d, у = %04Х", х, у);
134 Глава 3. Язык С и директивы препроцессора ПРИМЕЧАНИЕ В среде GCS-PICC также реализована функция get string (), которой в качестве первого параметра передается указатель на символьный массив (буфер для считывания строки), а в качестве второго — количество считываемых символов (длина строки). Директивы препроцессора Как уже было отмечено ранее, директивы препроцессора, по сути, не являют- ся составной частью языка С, а используются для подстановки кода в текст про- граммы. Препроцессор — это особая программа, которая выполняет предвари- тельную обработку данных до начала компиляции. Рассмотрим стандартные ди- рективы препроцессора, а также директивы, характерные для компилятора CCS- PICC. Директива ^include Выше, в разделе “Структуры программы на С” уже упоминалась самая рас- пространенная директива #include, которая используется фактически во всех программах для включения в текст программы заголовочных файлов (с определе- ниями), имеющий расширение . h, или файлов . с (не содержащих функцию main () ). Эта директива может использоваться в двух формах: #include <имя_файла> или #include "имя_файла" Если имя файла заключено между знаками “<” и “>”, то он считается частью стандартной библиотеки, поставляемой вместе с компилятором. Если же имя фай- ла заключено в двойные кавычки, то считается, что он расположен в той же папке, что и файл с исходным кодом. Директива #define Директива #define указывает препроцессору на необходимость выполнить подстановку в тексте программы определенной последовательности символов другой последовательностью. Формат директивы: # define заменяемая_последовательность фрагмент_подстановки Например: # define MyName "John Smith" # define Condition (a > b) #define Operation a = b char str[] = MyName; //Равнозначно char str[] = "John Smith"; int a, b; if Condition Operatrion; //Равнозначно if (a > b) a = b;
Директивы препроцессора 135 В директиве #define могут использоваться параметры, благодаря чему она становится очень мощный и гибким инструментом, позволяющим заменять один простой текстовый элемент сложным выражением. Такие подстановки называют макросами. Например, выражение, в котором выбирается большее из двух значений, можно представить в виде следующего макроса. #define larger(x, у) ( (х)>(у) ? (х) : (у) ) Если определен такой макрос, то код, использующий его, может иметь сле- дующий вид. int а - 9; int b = 7; inn с = 0; с = larger(а, Ь) ; Напоминает вызов функции, однако это не функция — компилятор получит от препроцессора последнюю строку в следующем виде. х с = ( (а) > (Ь) ? (а) : (Ь) ) ; Основное отличие макроса от функции заключается в том, что код макропод- становки вставляется препроцессором в программный код везде, где встречается заданный текстовый элемент, код же функции определяется только в одном месте, а в тех местах, где указано ее имя, осуществляется вызов этого кода. Есть и еще одно отличие, особенно важное при программировании микрокон- троллеров, — при использовании макросов, в отличие от функций, ничего не по- мещается в стек, что позволяет сэкономить оперативную память. Кроме того, макросы преобразуются в обычный код, который выполняется быстрее, чем код функции, на вызов которого и возврат управления в вызываю- щую функцию уходит дополнительное машинное время. Наконец, при использо- вании макросов не требуется формального объявления типов данных. Перечислим некоторые правила использования директивы #define: при создании комментариев в строке с #def ine всегда используется ком- ментарий вида/* ... */; следует помнить, что конец строки — это конец #def ine, и весь текст сле- ва заменить текст справа; для преобразования параметра макроса в текстовую строку можно указать перед ним символ например: #define OutString(s) puts(#s) OutString(Line); //Равнозначно puts("Line"); для конкатенации двух параметров можно воспользоваться оператором ##, например: #define Concat(х, у) х ## у int i = Concat(2,1); //Равнозначно int i = 21;
136 Глава 3. Язык С и директивы препроцессора для переноса текста подстановки на другую строку используется символ обратной косой например: #define LongStr "О 123456789 10 \ 11 12 13 14 15 16 17 18 19 20 " для отмены определения используется директива #undef, например: #define A_Char 'А' #undef A_Char #define A_Char 'a'. Директивы условной компиляции Многие микроконтроллеры отличаются лишь некоторыми параметрами, ко- личеством выводов, размером памяти и размещением регистров. Это позволяет создавать на языке С программный код для всего модельного ряда. Однако для этого следует каким-то образом заменить те параметры, которые отличаются у разных моделей. Для таких целей используются директивы условной компиляции #ifdef, #ifndef, #else и #endif, а также #if и #elif. Синтаксис для директивы ttifdef: #ifdef имя_макроса последовательность_операторов_1 #else последовательноеть_операторов_2 #endif Если имя макроса определено в программе, то компилируется первая после- довательность операторов, в противном случае — вторая последовательность (ветка #else может и отсутствовать). Пример использования (для компилятора CCS-PICC): ttdefine DEBUGGING_ON #ifdef DEBUGGING_ON fuse rs232 (baud=9600, xmit=PIN_C6, rcv=PIN__C7 ) #endif Синтаксис для директивы #ifndef: ttifndef имя_макроса последовательность_операторов_1 #else последовательность_операторов_2 #endif В данном случае, в отличие от директивы #ifdef, первая последовательность операторов выполняется в том случае, если имя макроса в программе не опреде- лено. Для условной компиляции можно также воспользоваться директивами #if, #elif. Их синтаксис:
Директивы препроцессора 137 # if выражение! последовательность_операторов_1 # elif выражение2 последовательность__операторов_2 #else последовательность_операторов_3 #endif Эта конструкция работает аналогично условному оператору if-else. Компи- лятор оценивает выражения после #if и #elif до тех пор, пока одно из них не даст в результате TRUE, после чего в текст программы подставляется соответст- вующая последовательность операторов. Если оба выражения дают false, то подставляется последовательность операторов после директивы #else (если она присутствует). Допустим, для передачи и приема данных через UART у абстрактного микро- контроллера SomeMicl6 используются выводы 12 и 13, у микроконтроллера SomeMicS — выводы 6 и 14, а у SomeMic4 — выводы 1 и 2. Тогда мы можем соз- дать заголовочный файл SomeMic. h и включить в него следующие директивы. # if SomeMicX == 16 #define TXD 12 #define RXD 13 #elif SomeMicX == 8 #define TXD 6 #define RXD 14 #elif SomeMicX == 4 # define TXD 1 # define RXD 2 #else terror "Pins TXD и RXD for SomeMicX are not defined" #endif Теперь, если программный проект будет построен на микроконтроллере SomeMicS, то в заголовочный файл следует поместить следующий текст. #ifndef SomeMicX # define SomeMicX = 8 #include <SomeMic.h> #endif Встретив директиву #ifndef, препроцессор включит в текст программы #define SomeMicX = 8 и #include <SomeMic. h>. Поскольку после этого эле- мент SomeMicX получит значение, равное 8, то любая повторная обработка дирек- тивы #ifndef не приведет к дублированию в выходном тексте соответствующей информации. Другими словами, содержимое заголовочного файла SomeMic.h бу- дет помещено в исходный код файлов, использующих заголовочный файл с #ifndef, только один раз. Использование директивы #ifndef с последующими директивами #define и #include — стандартный прием, позволяющий избежать дублирования инфор- мации из заголовочных файлов в исходном коде проекта. Если этого не сделать, то при дублировании компилятор обычно выдает множество сообщений об ошиб- ках, связанных с многократным объявлением переменных.
138 Глава 3. Язык С и директивы препроцессора Директива # err or Директива terror используется совместно с директивами условной компиля- ции. Встретив ее, компилятор сгенерирует сообщение об ошибке, указанное спра- ва от директивы (см. пример в предыдущем подразделе). Директивы, характерные для компилятора CCS-PICC Компилятор CCS-PICC поддерживает множество встроенных, нестандартных директив. Рассмотрим некоторые из них. Директивы #int_, #asm и ttendasm будут рассмотрены ниже в разделах “Обработка преры- ваний” и “Исполнение ассемблерного кода”. Директива #bit Директива препроцессора #bit используется для получения доступа к от- дельным разрядам регистров или переменных. Ее синтаксис: #bit идентификатор = х.у где х — константа, определяющая регистр, или переменная; у — константа от О до 7. Например, следующим образом переменная TOIF объявляется как разряд 2 регистра по адресу ОхОВ: #bit TOIF = ОхОВ.2 Пример для разряда переменной: inc с; #bit CBitO = с.0; if (CBitO ==0) ... Директива #byte Директива #byte имеет следующий синтаксис: #byte идентификатор = X где X — имя переменной или константы. Если идентификатор — это уже объявленное ранее имя переменной, то ком- пилятор помещает эту переменную по адресу х, в противном случае компилятор создает новую переменную и помещает ее по адресу X как 8-разрядное целое чис- ло. В обоих случаях в ячейку памяти с адресом х могут быть помещены любые другие переменные. Фактически, если X — имя переменной, то ей будет соответ- ствовать тот же адрес в памяти, что и для объявленного идентификатора: char с; //Переменная с размещается в памяти компилятором #byte а = с; //Переменной b назначен тот же адрес, что и с Директива #case Директива препроцессора #case указывает компилятору быть чувствитель- ным к регистру символов. По умолчанию, компилятор CCS-PICC не чувствителен к регистру символов.
Директивы препроцессора 139 ВНИМАНИЕ! Не все примеры программ, заголовочные файлы и драйверы были протестированы с вклю- ченной чувствительностью к регистру символов. Директива #device Директива #device определяет, какой микроконтроллер является целевым для программы. Ее синтаксис: #device имя__микроконтроллера опции Опции являются необязательными и влияют на управление памятью, аналого- цифровым преобразованием и возможностью отладки. Так, опции управления па- мятью позволяют пользователю задать количество разрядов, используемых для хранения указателя памяти. Возможные значения: *=5 (для всех семейств микро- контроллеров PIC) *=8 (для 14-ти- и 16-тиразрядных микроконтроллеров) и * = 16 (для 14-тиразрядных микроконтроллеров). 4 Например, следующая директива определяет для микроконтроллера PIC16F877 16-тиразрядный указатель памяти: #device PIC16F877 *=16 Для управления АЦП предназначена опция adc=x, где х — количество разря- дов, считываемых из преобразователя с помощью внутренней функции read_adc() . Если необходимо, чтобы сгенерированный код был совместим с отладочным программным обеспечением ICD от компании Microchip, в директиву #device следует включить опцию ICD=TRUE. ПРИМЕЧАНИЕ Если опции не указаны, то компилятор использует соответствующие значения, выбранные по умолчанию. Директива #fuse Директива #fuse определяет, какие предохранители должны быть установле- ны при программировании микроконтроллера. Ее синтаксис: #fuse опции Набор опций для различных устройств отличается. Для того чтобы просмот- реть список предохранителей для конкретного микроконтроллера, в среде разра- ботки CCS-PICC следует выполнить команду меню View ► Valid Fuses. К примеру, для микроконтроллера PIC18F458 используется следующий перечень предохрани- телей: BORV20 — сброс при падении питающего напряжения до 2 В; BORV27 — сброс при падении питающего напряжения до 2,7 В; BORV42 — сброс при падении питающего напряжения до 4,2 В; BORV45 — сброс при падении питающего напряжения до 4,5 В; BROWNOUT — сброс при обнаружении провала питания;
140 Глава 3. Язык С и директивы препроцессора СРВ — защита кода в блоке начальной загрузки; CPD — включена защита данных в памяти EEPROM; DEBUG —- активен режим отладки с помощью 1CD; ЕС — внешний источник синхроимпульсов с CLKOUT; ЕС10 — внешний источник синхроимпульсов; HS — высокоскоростной осциллятор с частотой > 4 МГц; LP — осциллятор малой мощности с частотой < 200 кГц; LVP — низковольтное программирование по выводу ВЗ (PIC 16) или В5 (PIC18); NOBROWNOUT — сброс при провале питания не используется; NOCPB — защита кода в блоке начальной загрузки отключена; NOCPD — защита данных в памяти EEPROM отключена; NODEBUG — отсутствие режима отладки для ICD; NOLVP — низковольтное программирование не используется; вывод ВЗ (PIC16) или В5 (PIC18) задействован для ввода/вывода; NOOSCSEN — переключение осциллятора запрещено; NOPROTECT — код не защищен от чтения; NOPUT — таймер включения питания не используется; NOSTVREN — переполнение или выход за нижнюю границу стека не при- водит к сбросу; NOWDT — сторожевой таймер не используется; NOWRT — защита от записи в память программ отключена; NOWRTB — защита от записи в блок начальной загрузки отключена; NOWRTC — защита от записи в регистры конфигурации отключена; NOWRTD — защита от записи данных в память EEPROM отключена; OSCSEN — активно переключение осциллятора; PROTECT — защита кода от чтения; PUT — используется таймер включения питания; RC — RC-осциллятор с CLKOUT; RC10 — RC-осциллятор; STVREN — переполнение или выход за нижнюю границу стека приводит к сбросу; WDT — используется сторожевой таймер; WDT1 — сторожевой таймер использует постделитель 1:1; WDT2 — сторожевой таймер использует постделитель 1:2; WDT4 — сторожевой таймер использует постделитель 1:4; WDT8 — сторожевой таймер использует постделитель 1:8; WDT16 — сторожевой таймер использует постделитель 1:16; WDT32 — сторожевой таймер использует постделитель 1:32;
Директивы препроцессора 141 WDT64 — сторожевой таймер использует постделитель 1:64; WDT128 — сторожевой таймер использует постделитель 1.128; WRT — защита от записи в память программ; WRTB — защита от записи в блок начальной загрузки; WRTC — защита от записи в регистры конфигурации; WRTD — защита от записи данных в память EEPROM; XT — осциллятор на кристалле с частотой <= 4 МГц. Так, следующий пример директивы #fuses устанавливает высокоскоростной осциллятор, активизирует сброс при обнаружении провала питания и отключает сторожевой таймер: #fuses HS, BROWNOUT, NOWDT Директива #locate Директива препроцессора #locate по своему назначению и синтаксису ана- логична директиве #byte за тем исключением, что назначает переменной ячейку памяти, в которую не может быть помещена никакая другая переменная. Напри- мер: #locate с = 0x50; //Размещение переменной с по адресу 0x50 Директива #org Директива #org позволяет пользователю компилятору определить, где долж- на быть размещена в памяти следующая ниже функция. Она имеет синтаксис че- тырех видов: #org начало, конец #org сегмент #org начало, конец {} #org начало, конец auto=0 где начало всегда определяет первую, а конец — последнюю ячейку в исполь- зуемой области ROM; сегмент — начало области ROM из предыдущей директи- вы #org. Если ранее был определен некоторый сегмент, и необходимо только до- бавить к нему еще одну функцию, идентификатор окончания может быть опущен. Пример использования директивы #org: #org OxlEOO, OxlFFF fl 0 { //Эта функция будет размещена, начиная с адреса OxlEOO } #org OxlEOO //Сегмент - тот же, который был определен выше f 2 () { //Эта функция будет размещена в сегменте OxlEOO-OxlFFF
142 Глава 3. Язык С и директивы препроцессора #org 0x800, 0x820 {} //Пустой сегмент Директива #opt Директива #opt задает уровень оптимизации, применяемой при компиляции. Она имеет синтаксис #opt уровень где уровень — число от 0 до 9 (9—- полная оптимизация). Директива #priority Директива препроцессора #priority может быть использована для установ- ки порядка, в котором опрашиваются флаги прерываний. Некоторые микрокон- троллеры PIC имеют только один вектор прерываний. Когда возникает прерыва- ние, управление передается по адресу, указанном в векторе. По умолчанию, ком- пилятор CCS-PICC помещает по этому адресу диспетчерскую подпрограмму, ко- торая опрашивает флаги прерываний, чтобы определить, какое из них имело ме- сто, и вызвать соответствующую подпрограмму обслуживания прерывания. Обработка прерываний рассматривается в следующем разделе этой же главы. Синтаксис директивы: #priority список_прерываний Пример использования: ftpriority rtcc, rb ПРИМЕЧАНИЕ Прерывания с большим приоритетом расположены в списке первыми. Директива #reserve Директива #reserve определяет области памяти RAM, не доступные для ис- пользования компилятором. Ее синтаксис имеет две формы: preserve адрес, адрес, адрес ... preserve начальный_адрес_диапазона:конечный_адрес_диапазона ПРИМЕЧАНИЕ Директиве #reserve должна предшествовать директива #device, иначе она не будет иметь никакого эффекта. Примеры резервирования области 0x20-0x22: ftreserve 0x10, 0x11, 0x12 #reserve 0x10:0x12 Директива #rom Директива препроцессора #rom позволяет вставлять данные в файл .hex. Ее синтаксис:
Директивы препроцессора 143 #rom адрес = {список} где адрес — адрес в ROM, а список — список слов через запятую. Пример: #гого 0х2100={1, 2, 3, 4, 5, 6, 7, 8} Директива #type Директива препроцессора #type позволяет переопределять типы, поддержи- ваемые компилятором. Ее синтаксис: #type стандартный_тип=размер, стандартный_тип=размер, ... К примеру, по умолчанию, компилятор CCS-PICC отводит под переменную типа char 8 бит, а пот переменную типа int — 16 бит (см. табл. 3.2). Для того чтобы изменить эту ситуацию, в текст программы можно добавить следующую директиву: #type char=16, int=32 Директива #use delay Директива #use delay предназначена для задания рабочей частоты процес- сора и разрешает использование в программе внутренних функций delay_ms() (задержка в миллисекундах) и delay_us () (задержка в микросекундах). Ее син- таксис имеет две формы: #use delay(частота) #use delay(частота, restart_WDT) Частота указывается в герцах. С помощью опции restart_DWT можно ука- зать компилятору перезагружать сторожевой таймер во время программных за- держек. Директивы #use xxx_io Директивы вида #use xxx_io влияют на код, сгенерированный при реализа- ции доступа к портам ввода/вывода, и имеют отношение к установке регистров TRIS. В случае стандартного доступа на ввод/вывод (выбор по умолчанию), ком- пилятор генерирует код, переводящий некоторый вывод микроконтроллера в со- стояние входа или выхода при каждом обращении к нему. Такой способ доступа активизируется директивой препроцессора #use standard_io: #use standard__io (X) где x — символ порта (A...G). Если пользователь должен устанавливать разряды регистров TRIS вручную перед обращением к соответствующим выводам напрямую или с помощью встро- енной функции set tris x (), то в программе должна быть указана директива препроцессора #use fast_io: #use fast_io(X) где X — символ порта (A...G). Подобная по смыслу директива #use fized_io принимает в качестве пара- метров идентификаторы выводов, устанавливаемых в качестве входов/выходов: #use fixed_io (Х_ои1ри1з=вывод, вывод, вывод...)
144 Глава 3. Язык С и директивы препроцессора где X — символ порта (а...д), вывод — константа, соответствующая выводу (пере- чень таких констант можно найти в заголовочном файле устройства). Примеры для вывода 0 порта В: # use standard_io(В) # use fast_io(В) # use fixed_io(b_outputs=PIN_BO) Директива #use i2c Директива #use i2c определяет порт I2C микроконтроллера. Ее синтаксис: #use i2c(опции) В качестве опций (указываются через запятую) могут использоваться сле- дующие значения: ADDRESS=rm — задание адреса ведомого устройства; FAST — использование спецификации быстрой шины 12С; FORCE HW — использование аппаратных функций 12С; master — установка режима ведущего устройства; restart__wdt — перезагрузка сторожевого таймера во время ожидания в процессе выполнения встроенной функции i2c_read (); зсь=вывод — задание вывода SCL (вывод — адрес разряда); 30А=вывод — задание вывода SDA; slave — установка режима ведомого устройства; slow — использование спецификации медленной шины 12С. В следующем примере в качестве линии данных устанавливается вывод О порта В, а в качестве тактирующей линии — вывод 1 того же порта (режим веду- щего устройства): #use i2c(MASTER, sda=PIN_PIN_BO, scl=PIN_Bl) Пример для режима ведомого устройства: #use i2c(SLAVE, sda=PIN_C4, scl=PIN_C3, ADDRESS=0xA0, FORCE_HW) ПРИМЕЧАНИЕ Директива i jc считается активной до тех пор, пока в тексте программы не встретит- ся другая директив #use i2c. Эта директива позволяет воспользоваться встроенными функциями, реали- зующими обмен данными по шине 12С (если включена опция FORCE HW, то про- граммные функции не генерируются и используется аппаратный порт MSSP). Директива #use rs232 Директива препроцессора #use rs232 используется для инициализации по- следовательного порта, работающего по стандарту RS232. В зависимости от того,
Обработка прерываний 145 как заданы выводы передачи и приема, реализация порта может быть аппаратной или программной. Если выводы соответствуют приемопередатчику USART, то используется аппаратный порт, в противном случае— программный UART. Общая форма директивы #use rs232: #use rs232 (опция, опция, опция, ...) Возможны следующие опции: BAUD=x — установка скорости передачи; BlTS=x — установка разрядности в х, где х — 5-9 в случае программного UART и 8-9 в случае аппаратного UART; ЕМАВЬЕ=вывод — во время передачи компилятор переводит указанный вывод в состояние высокого уровня; errors — ошибки приема сохраняются в переменной RS232 ERRORS; FLOAT HIGH — использование на выходе схем с открытым коллектором; INVERT — инвертирует полярность выводов последовательного порта (ис- пользуется только с программным приемопередатчиком UART); PARITY=x — контроль по четности: x=N — отсутствует; х=Е — контроль по четности; х=О — контроль по нечетности; ЕС7=вывод — установка вывода для приема данных; restart wdt — сброс сторожевого таймера при ожидании символа в функции getchar (); ЗТЕЕАМ=имя_потока — ассоциирует идентификатор потока с портом RS232 (этот идентификатор может использоваться в функциях, наподобие fputc ( ) ). хм!Т=вывод — установка вывода для передачи данных. ПРИМЕЧАНИЕ При выборе скорости передачи следует учитывать частоту системной синхронизации мик- роконтроллера. По этой причине перед директивой #use rs232 обязательно должна быть указана директива #use delay. Если указанная скорость не может быть достигнута (с от- клонением 3%), то генерируется сообщение об ошибке. Директива # zero_ram Директива препроцессора #zero_ram указывает компилятору сбрасывать пе- ред началом выполнения программы все внутренние регистры, которые могут быть использованы для хранения переменных, в нуль. Обработка прерываний Обработка прерываний в среде WinA VR В различных компиляторах обработка прерываний реализована по-разному. Так, в среде WinAVR используется предустановленная таблица векторов преры- ваний, содержащая адреса соответствующих подпрограмм обслуживания с зара- 10-6-767
146 Глава 3. Язык С и директивы препроцессора нее определенными именами. Для каждой такой подпрограммы в библиотечном файле \avr\signal.h определены два макроса: INTERRUPT () и SIGNALO. Эти макросы регистрируют и помечают некоторую функцию как обработчик прерыва- ния. Их различие в том, что макрос INTERRUPT () определяет функцию обработ- чика для случая, когда разрешено общее прерывание (т.е, обработчик может быть прерван), a signal () — для случая, когда общее прерывание запрещено (т.е, об- работчик не может быть прерван). Например, так выглядит определение обработчика для прерывания от АЦП: #include <avr/signal.h> INTERRUPT(SIG_ADC) { // Здесь вносится пользовательский код } или #include <avr/signal.h> SIGNAL(SIG-ADC) { // Здесь вносится пользовательский код } Если возникает неожиданное прерывание (то есть, такое, для которого не оп- ределен обработчик), по умолчанию происходит сброс микроконтроллера в ре- зультате перехода по соответствующему вектору. Во избежание этого, следует пе- рекрыть библиотечную функцию __vector default, что можно сделать с по- мощью макроса signal (): #include <avr/signal.h> SIGNAL(___vector_default) { // Здесь вносится пользовательский код } В качестве идентификаторов прерываний, передаваемых в качестве парамет- ров макросам INTERRUPT и SIGNAL, могут служить следующие: SIG_2WIRE SERIAL — двухпроводной последовательный интерфейс (12С); SIG_ADC — аналого-цифровое преобразование завершено; SIG_COMPARATOR — прерывание от аналогового компаратора; sig_eeprom_ready — память EEPROM готова; SIG_FPGA_INTERRUPTO - SIG_FPGA_INTERRUPT15; SIG_INPUT_CAPTURE1 - SIG_INPUT_CAPTURE3 — прерывание по захвату на входе; SIG_INTERRUPTO - SIG_interrupt7 — внешнее прерывание; SIG_OUTPUT_COMPAREO - SIG_OUTPUT_COMPARE2 — прерывание по срав- нению на выходе;
Обработка прерываний 147 SIG_OUTPUT_COMPARE1A - SIG_OUTPUT_COMPARE3A— прерывание ПО совпадению с регистром сравнения А; SIG_output_compare1b - sig_output_COMPARE3b — прерывание по совпадению с регистром сравнения В; sig_output_compareic - sig_output_compare3C — прерывание по совпадению с регистром сравнения С; SIG—OVERFLOWO - sig_overflow3 — прерывание по переполнению счет- чика; SIG_PIN — прерывание по сигналу на выводе параллельного порта; SIG_PIN_CHANGEO, SI G__P IN_C RANGE 1 — прерывание по изменению уров- ня на выводе параллельного порта; SIG_SPI — прерывание от SPI; SIG_SPM_READY — память программ готова; sig_suspend_resume; SIG_UARTO — прерывание от UART(O); SIG_UARTO_DATA— прерывание по опустошению регистра данных UART(O); SIG UARTO RECV — прерывание по завершению приема UART(O); sig_uartO_trans — прерывание по завершению передачи UART(O); SIG_UART1 — прерывание от UART(l); SIG_UART1_DATA— прерывание по опустошению регистра данных UART(l); SIG UART1RECV — прерывание по завершению приема UART(l); SIG UART1TRANS — прерывание по завершению передачи UART(l); IG_UART_DATA — прерывание по опустошению регистра данных UART; SIG_UART_RECV — прерывание по завершению приема UART; SIG_UART_TRANS — прерывание по завершению передачи UART; IG_USARTO_DATA — прерывание по опустошению регистра данных USART(O); SIG USARTO RECV — прерывание по завершению приема USART(O); IG USARTO TRANS — прерывание по завершению передачи USART(O); SIG_USART1_DATA — прерывание по опустошению регистра данных USART(l); sIG_USART 1_recv — прерывание по завершению приема USART(l); SIG USART1TRANS — прерывание по завершению передачи USART(l); sig_usb_hw — прерывание от порта USB. Кроме того, в библиотечном файле \avr\interrupt. h объявлены макро- функции sei () и cli (). Первая устанавливает флаг общего разрешения преры- ваний в регистре управления SREG, а вторая, наоборот, — сбрасывает его. '5Ьг Подробнее эти макроопределения рассмотрены в приложении Д. 10*
148 Глава 3. Язык С и директивы препроцессора Обработка прерываний в среде CCS-PICC В среде компилятора CCS-PICC для определения подпрограмм обработки прерываний используются директивы препроцессора #int_default, #int_global и #int_xxx (указываются непосредственно перед функцией). Во многих микроконтроллерах PIC используется единственный вектор прерывания, и, следовательно, в случае возникновения прерывания вызывается только одна подпрограмма, называемая диспетчером прерываний. Диспетчер отвечает за опрос флагов прерываний и вызова соответствующих подпрограмм обслуживания. Если обнаружено прерывание, и ни один из флагов прерывания не установ- лен, то диспетчер вызывает функцию, обозначенную с помощью директивы #int_default. Директива #int_global позволяет определить функцию, заменяющую дис- петчер компилятора (такие функции используются крайне редко, и с ними следует соблюдать большую осторожность). Директива #int_xxx определяет функцию, отвечающую за обслуживание того или иного прерывания. Перечислим наиболее распространенные директивы этого типа: # int_ad — аналого-цифровое преобразование завершено; # int_adof — задержка аналого-цифрового преобразования; # int_buscol —конфликт шины; # int button — нажатие кнопки; # int_ccpl — захват или совпадение в модулей ССР1; # int_ccp2 — захват или совпадение в модулей ССР2; # int_comp — прерывание от аналогового компаратора; # int_eeprom — запись в EEPROM завершена; # int_ext — внешнее прерывание; # int_extl —внешнее прерывание 1; # int__ext2 — внешнее прерывание 2; # int 12с — прерывание от шины ГС; # int_lcd — ЖК-дисплей активен; # int_lowvolt — обнаружено низкое напряжение; # int_psp — запись данных в порт PSP; # int_rb — изменение состояния выводов 4-7 порта В; # int_rc — изменение состояния выводов 4-7 порта С; # int_rda — доступен прием данных по интерфейсу RS232; # int_rtcc — переполнение таймера TMR0; # int_ssp — активность интерфейса SPI или 12С; # int_tbe — буфер передачи по интерфейсу RS232 пуст; # int_timerO — переполнение таймера TMR0; # int_timerl — переполнение таймера TMR1; # int_timer2 — переполнение таймера TMR2; # int_timer3 — переполнение таймера TMR3.
Исполнение ассемблерного кода 149 В ряде микроконтроллеров PIC (например, в PIC18F458) используется два вектора прерываний. Это позволяет установить высокий приоритет некоторому прерыванию на аппаратном уровне. Компилятор CCS-PICC использует эту аппа- ратную возможность с помощью опции FAST директивы #int_xxx: #int ххх FAST ВНИМАНИЕ! Этот уровень приоритета можно назначить только одному прерыванию, поэтому флаг fast может быть использован в программе только один раз. В случае использования флага fast соответствующее прерывание не сохра- няет всех регистров так, как это делает обычное прерывание. Сохраняются только основные регистры процессора. Таким образом, к примеру, можно быстро выпол- нять какие-то очень простые операции. Исполнение ассемблерного кода Иногда в программах, разрабатываемых на языке С, требуется напрямую ис- пользовать команды ассемблера с целью оптимизации кода или просто из-за не- возможности выполнить те или иные операции средствами С. Как компилятор WinAVR, так и CCS-PICC поддерживают вставку в исходный текст программы фрагментов на ассемблере. Рассмотрим эту возможность подробнее. Ассемблерные команды для микроконтроллеров AVR и PIC перечислены в приложениях В и Г. Использование ассемблера в компиляторе WinA VR В программах, разрабатываемых с помощью средств WinAVR, для выполне- ния ассемблерного кода доступен специальный оператор asm. Его синтаксис: asm(код: список_выходных_операндов, список_входных_операндов) Поясним использование оператора asm на простом примере чтения значения из порта D: asm("in %0, %1" : "=r" (value) : "I" (_SFR_IO_ADDR(PORTD)) ); Смысл каждой части оператора asm, отделенной с помощью двоеточия: 1. Ассемблерная команда, определенная как строковая константа: "in %0, %1". 2. Список выходных операндов, разделенных запятыми. В нашем примере есть только один операнд: "=r" (value). 3. Список входных операндов, разделенных запятыми. В нашем примере есть только один операнд: "I" (_SFR_IO_ADDR(PORTD) ). Вторая и третья части оператора asm предназначены для определения связи между регистрами микроконтроллера и операндами С. В самих ассемблерных ин- струкциях ссылки на операнды создаются с помощью символа “%” и порядкового номера операнда (начиная с нуля). Так, в рассмотренном выше примере ссылке % О соответствует входной операнд "=r" (value), а ссылке %1 — выходной операнд "I" (_SFR_IO_ADDR(PORTD)) .
150 Глава 3. Язык С и директивы препроцессора Синтаксис операндов мы рассмотрим чуть позже, а пока исследуем часть ас- семблерного листинга, который мог быть получен в результате компиляции пред- ставленного выше оператора asm: in r24, 12 В данном случае для хранения значения, считанного из порта D компилятор выбрал регистр г24, хотя это мог бы быть и любой другой регистр. Компилятор мог бы даже выполнить неявную загрузку или сохранение значения или решить вообще не включать пользовательский ассемблерный код. Все эти решения — часть оптимизационной стратегии компилятора. Напри- мер, если бы значение переменной ни разу не использовалось в оставшейся части программы, то представленный выше код, скорее всего, был бы исключен. Во из- бежание подобного, к оператору asm следует добавить атрибут volatile: asm volatile("in %0, %1" : "=r" (value) : "I" (_SFR_IO_ADDR(PORTD))); Если ассемблерные инструкции не используют операндов, то соответствую- щие части оператора asm могут быть опущены. Например, в случае общего запре- та прерываний это будет выглядеть следующим образом: asm volatile("cli; Ассемблерный код В первой части оператора asm можно использовать любые команды AVR- ассемблера. При этом для улучшения удобочитаемости каждую инструкцию мож- но помещать в отдельную строку с помощью символьных литералов перевода строки: asm volatile("nop\n" "nop\n" "nop\n" "nop\n" : : ) ; Кроме того, можно использовать некоторые специальные символы, соответ- ствующие тем или иным регистрам микроконтроллера: __SREG___— регистр состояния; __SP_H___— старший байт указателя стека; __SP L___— младший байт указателя стека; __tmp reg___— регистр г0, используемый для промежуточного хранения; __zero reg — регистр г 1, всегда нулевой. Входные и выходные операнды Каждый входной и выходной операнд описывается строкой уточнений, после которой следует выражение языка С в круглых скобках. Компилятор WinAVR поддерживает уточнения, перечисленные в табл. 3.4.
Исполнение ассемблерного кода 151 Таблица 3.4. Уточнения в определении входных операндов оператора asm Уточнение Использование Диапазон значений a Обычные старшие регистры г16..г23 b Регистры двойной длины для указателя базы У. z d Старшие регистры г16..г31 e Регистры двойной длины — указатели х, у, z G Вещественная константа 0,0 I Шестиразрядная положительная целая константа 0..63 J Шестиразрядная отрицательная целая константа -63..0 К Целая константа 2 L Целая константа 0 1 Младшие регистры г0..г15 M Восьмиразрядная целая константа 0..255 N Целая константа -1 0 Целая константа 8, 16, 24 P Целая константа 1 q Указатель стека SPH:SPL r Любой регистр г0..г31 t Временный регистр г0 w Специальные старшие регистровые пары г24, г26, г28, гЗО X Регистр-указатель двойной длины X х (г27:г26) У Регистр-указатель двойной длины Y у (г29:г28) z Регистр-указатель двойной длины Z z (г31 :г30) Символам уточнений могут предшествовать модификаторы (если модифика- тор не указан, операнд считается “только для чтения”): = — операнд “только для записи” (для всех выходных операндов); & — регистр должен использоваться только для вывода. Входные операнды — только для чтения. Но что делать, если необходимо, чтобы один и тот же операнд был и входным, и выходным одновременно? Для этого во входном операнде можно использовать в качестве уточнения цифру, со- ответствующую порядковому номеру выходного операнда. Например: asm volatile("swap %0" : "=r" (value) : "0" (value)); Этот оператор поменяет местами полубайты восьмиразрядной переменной value. Ограничитель "О" указывает компилятору использовать тот же входной регистр, что и первый операнд. В тех случаях, когда код реализует различные регистры, используемые для входных и выходных операндов, к выходному операнду следует добавить моди- фикатор &. Например: asm volatile ("in р50,%1" "\n\t" "out %1, %2" "\n\t" : " = &r" (input) : "I" (_SFR_IO_ADDR(port)), "r" (output)
152 Глава 3. Язык С и директивы препроцессора В этом примере входное значение считывается из порта, а затем выходное в тот же самый порт записывается значение. Если бы компилятор выбрал для вво- да и вывода один и тот же регистр, то после выполнения первой ассемблерной ко- манды выходное значение было бы потеряно. Благодаря использованию модифи- катора &, компилятор распознал, что для выходного значения следует использо- вать любой регистр, не занятый под входные операнды. Возвращаясь к примеру перестановки, код перестановки старшего и младшего байта некоторого 16-тираз- рядного значения будет выглядеть следующим образом: asm volatile("mov ___tmp_reg___, %A0" "\n\t" "mov %A0, %B0" "\n\t" "mov %B0, ___tmp_reg__" "\n\t" : "=r" (value) : "0" (value) Пример перестановки байтов 32-хбитного значения: asm volatile("mov tmp_reg_, %A0" "\n\t" "mov %A0, %D0" "\n\t" "mov %D0, ___tmp reg " "\n\t" "mov tmp_reg__, %B0" "\n\t" "mov %B0, %C0" "\n\t" "mov %C0, ___tmp_reg___" "\n\t" : "=r" (value) : "0" (value) Если операнды не помещаются в один регистр, компилятор автоматически на- значит дополнительные регистры, общий размер которых будет достаточным для хранения всего операнда. В рассмотренных примерах спецификации %А0 соответ- ствует младший байт первого операнда, а спецификации %А1 — младший байт второго операнда. Следующему байту первого операнда соответствует % ВО, сле- дующему — % СО и т.д. Резервирование регистров В том случае, если в операторе asm должны быть указаны регистры, которые не передаются в качестве операндов, об этом следует каким-то образом уведомить компилятор. Для этой цели служит еще одна, четвертая часть оператора asm, ко- торая в общем случае является необязательной, — часть резервированных регист- ров. Рассмотрим пример реализации автоинкремента восьмиразрядного значения, на которое указывает переменная-указатель, без прерывания какой-либо подпро- граммой обслуживания прерывания или параллельным процессом (мы должны использовать указатель, поскольку инкрементированное значение должно быть сохранено до разрешения прерываний). asm volatile( "cli" "Id r24, %a0" "inc r24" "st %a0, r24" "\n\t" "\n\t" "\n\t" "\n\t"
Исполнение ассемблерного кода 153 'sei \n\t : "е" (ptг) : "г24" В результате компилятор сгенерирует следующий ассемблерный код: cli Id r24, Z inc r24 st Z, r24 sei Для того чтобы избежать резервирования регистра г24, можно воспользовать- ся специальным буферным регистром___tmp^reg___: asm volatile( "cli" "Id __tmp_reg___, %a0" "inc _tmp_reg___" "st %a0, __tmp_reg__" "\n\t" "\n\t" "\n\t" "\n\t" "\n\t" sei " "e" (ptr) Еще одна проблема заключается в том, что рассматриваемый код не может быть использован в тех программных секциях, где прерывания запрещены и не должны активизироваться, поскольку в конце используется команда общего раз- решения прерываний sei. Конечно, можно сохранять текущее состояние микро- контроллера, однако в таком случае потребуется еще один регистр. Опять таки, это можно реализовать без резервирования фиксированного регистра, а с помо- щью локальной переменной языка С: unsigned char s; asm volatile( "in %0, ___SREG_" "cli" "Id ___tmp_reg__, %al" "inc __tmp_reg__" "st %al, tmp—reg " "out _SREG____, %0" : "=&r" (s) : "e" (ptr) "\n\t" "\n\t" "\n\t" "\n\t" "\n\t" "\n\t" Теперь ассемблерный код модифицирует переменную, на которую указывает ptr, однако компилятор может этого не распознать и сохранить значение в каком- либо другом регистре. Кроме того, значение переменной может модифицировать сама программа на С, а компилятор не обновит ячейку памяти по причинам опти-
154 Глава 3. Язык С и директивы препроцессора мизации. Во избежание подобных проблем можно воспользоваться специальным резервирующим определением "memory": { unsigned char s; asm volatile( "in %0, SREG " "\n\t" "cli" "\n\t" "Id tmp__reg , %al" "\n\t" "inc tmp_reg " "\n\t" "st %al, tmp__reg " "\n\t" "out SREG , %0" "\n\t" : "=&r" (s) : "e" (ptr) : "memory" ) ; Определение "memory" сообщает компилятору о том, что ассемблерный код может модифицировать любую ячейку памяти. В результате компилятор перед выполнением ассемблерного кода будет обновлять все переменные, содержимое которых уже содержится в регистрах. И, конечно же, после выполнения этого кода все регистры будут восстановлены в исходное состояние. Использование ассемблера в компиляторе CCS-PICC В программах, разрабатываемых в среде CCS-PICC для вставки ассемблерных фрагментов используются директивы препроцессора #asm и ttendasm: #asm ассемблерный код #endasm Если требуется, что компилятор не выполнял автоматического переключения банков памяти для переменных, к которым нельзя получить доступ из текущего банка, в директиве #asm следует использовать опцию ASIS: #asm ASIS ассемблерный код ttendasm Без опции ASIS выполняется расширенное ассемблирование, в результате ко- торого доступ к переменным всегда реализуется корректно, благодаря добавлению (там, где это необходимо) переключения банков. Для назначения возвращаемого функцией значения непосредственно в ас- семблерном блоке можно воспользоваться предустановленной переменной _return_: int find_parity(int data) { int count; #asm movlw 0x08 movwf count
Исполнение ассемблерного кода 155 movlw О loop: xorwf data, w rrf data, f decfsz count, f goto loop movwf _return_ frendasm } Следует помнить, что любой код между директивой #endasm и окончанием функции может нарушить значение переменной return .
Глава 4 Программные примеры для микроконтроллеров AVR При использовании примеров, представленных в этой главе, может потребо- ваться изменить значения временных задержек перед записью программы в ко- нечное устройство. Не забывайте также указывать корректный тип микроконтрол- лера в параметре MCU файла makefile. Вывод некоторого числа по нажатию кнопки К порту А подключены кнопочные переключатели, к порту В — светодиоды. Пока не нажата какая-либо кнопка, светоиндикаторы загораются по очереди, соз- давая как бы “бегущую точку”. В то же время подсчитывается некоторое число, которое при нажатии любой кнопки отображается в двоичной форме с помощью светоиндикаторов, а также выводится в символьном виде через приемопередатчик UART. Реализация — в листинге 4.1. , Файлы для этого примера btnnums . с и makefile находятся на прилагаемом к книге ком- W пакт-диске в папке Projects\AVR\BtnNumS. Листинг4.1. Файл btnnums.с #include <avr/io.h> #include <avr/delay.h> #include <stdio.h> char n; //Отображаемый номер char с; //Счетчик текущего светоиндикатора в режиме ожидания int main (void) { UBRR = 25; //Скорость обмена через UART - 9600 бод //(см. табл. 1.17) UCR = 0x18; /*Устанавливаем разряды TXEN и RXEN для активизации UART в режиме ввода/вывода через выводы 0 и 1 порта D. Разряд CHR9=0 - передача 8 бит данных */ DDRA = 0x00; //Все выводы порта А - входы PORTA = OxFF; //Выводы порта А - нагруженные DDRB = OxFF; //Все выводы порта В - выходы while (1) //Бесконечный цикл { с = 1; //Начинаем с первого светоиндикатора while(PINA == OxFF) //До тех пор, пока не нажата кнопка... {
Бегающие “глаза” 157 Листинг 4.1. Окончание if (с == 0) с = 1; //Если "точка" пробежала всю линейку //индикаторов, то начинаем опять с первого светодиода PORTB = ~с; //Загорается "бегущая точка" _delay_loop_2(100); //Короткая задержка п++; //Увеличиваем число, предназначенное к выводу с = с « 1; //Сдвигаем "бегущую точку" влево } printf("%d", n) ; //Если нажата кнопка, выводим число через PORTB = ~n; //UART и отображаем его с помощью светодиодов _delay_loop_2(1000); //Длинная задержка PORTB = OxFF; //Гасим светодиоды } } Бегающие “глаза” К выводам порта В подключены переключатели, а к выводам порта D — све- тодиодные индикаторы. Эффект “бегающих глаз” может быть создан путем по- очередного включения двух светодиодов при выключенных остальных или, на- оборот, — поочередного выключения двух светодиодов при включенных осталь- ных. Один из этих двух типов выбирается с помощью переключателя, подсоеди- ненного к выводу 7 порта В. Остальные входы этого порта определяют скорость “бега” (коэффициент от 0 до 127). Реализация этого алгоритма — в листинге 4.2. Файлы для этого примера RunEyes . с и makefile находятся на прилагаемом к книге ком- 'ЧИК' пакт-диске в папке Pro j ects\AVR\RunEyes. Листинг 4.2. Файл RunEyes. с #include <avr/io.h> #include <avr/delay.h> unsigned long DelayCount; // Переменная DelayCount - значение //временной задержки, определяющей скоростью "бега глаз unsigned long Velocity =0; // Переменная для скорости unsigned char EyeType =0; // Переменная для типа "глаз" //Функция, отображающая "глаза" с помощью светодиодов void ShowEyes(int i) { //В зависимости от типа "глаз", включаем или гасим светодиоды if(EyeType) PORTD = ~i; // Инвертируем I перед выводом в порт else PORTD = i; // Прямая запись i в порт _delay_loop_2(DelayCount); //Задержка } int main (void) {
158 Глава 4. Программные примеры для микроконтроллеров AVR Листинг 4.2. Окончание DDRB = 0x00; // Все выводы порта В - входы DDRD = OxFF; // Все выводы порта D - выходы while(1) { Velocity = // PINE Бесконечный цикл 1; // Считываем значения переключателей if(Velocity > 127) //Если "скорость" > 127, значит { //переключатель на выводе 7 отключен Velocity -= 127; //Получаем реальную "скорость" (без бита 7) ЕуеТуре =1; //Бегают "глаза" в виде светящихся точек } else ЕуеТуре = 0; //Если переключатель на выводе 7 //включен,то бегают "глаза" в виде пробелов DelayCount = 500 + (Velocity * 50) ; //Устанавливаем "скорость" /* "Глаза" сначала бегут справа налево, а затем - наоборот: при i = l "глазам" соответствует двоичное число 00010001 (17); при i=2 "глазам" соответствует двоичное число 00100010 (34); при i=4 "глазам" соответствует двоичное число 01000100 (68); при i=8 "глазам" соответствует двоичное число 10001000 (136) */ for(int i = 1; i <= 8; i = i*2) ShowEyes(i * 16 + i); for(int i = 8; i > 1; i i/2) ShowEyes(i * 16 + i); Индикатор, мигающий каждую секунду К выводу 0 порта В подключен светодиодный индикатор, который должен пе- реключаться каждую секунду. Это можно сделать с помощью прерывания при пе- реполнении таймера (см. также раздел “Обработка прерываний” в конце преды- дущей главы). Воспользуемся, например, таймером/счетчиком Т/С1. С помощью разрядов CS10-CS12 регистра TCCR1B устанавливается коэффи- циент деления частоты системной синхронизации для тактирования таймера. Вы- берем коэффициент 1024, что соответствует значению 0Ы01 или просто 5 (см. табл. 1.5). Таким образом, в случае рабочей частоты микроконтроллера в 4 МГц значение счетного регистра TCNT1 будет увеличиваться через каждые 1024 / 4000000 = 0,000256 с. Следовательно, необходимо установить такое стартовое значение счетчика, чтобы до его переполнения отсчитывалось 1 / 0,000256 - 3906 импульсов. Поскольку с помощью 16 разрядов счетчика можно получить макси- мальное значение 65535 (OxFFFF), то он должен инициализироваться значением 65536-3906. Реализация рассмотренного алгоритма представлена в листинге 4.3. faOW Файлы для этого примера LEDBlink. с и makefile находятся на прилагаемом к книге ком- Wk7 пакт-диске в папке Projects\AVR\LEDBlink. Листинг4.3. Файл bEDBlink.а #include <avr/io.h> #include <avr/interrupt.h> //Для доступа к функции sei()
Переключение индикаторов по нажатию кнопки 159 Листинг 4.3. Окончание #include <avr/signal.h> //Для доступа к макросу INTERRUPT tfdefine Freq 4000000 //Частота системной синхронизации в Гц INTERRUPT(SIG_OVERFLOW1) //Подпрограмма обработки прерывания по { //переполнению счетчика Т/С1 //Инициализируем счетный регистр значением 61630 TCNT1 = 0x10000 - (Freq / 1024); PORTB л= 0x01; //Меняем состояние вывода 0 порта В } int main (void) { DDRB = 0x01; //Вывод 0 порта В — выход TCCR1A = 0; //Т/Cl отсоединен от вывода ОС1, режим ШИМ отключен TCCR1B = 5; //Коэффициент деления частоты системной //синхронизации = 1024 //Инициализируем счетный регистр значением 61630 TCNT1 = 0x10000 - (Freq / 1024); TIFR = 0; //Сбрасываем все флаги прерываний от Т/С1 TIMSK = 0x80; //Разрешаем прерывание при переполнении Т/С1 GIMSK = 0; //Запрет внешних прерываний sei(); //Общее разрешение прерываний while(1) ; //Ожидаем прерывания от Т/Cl каждую секунду } Переключение индикаторов по нажатию кнопки К выводам порта В подключены светодиодные индикаторы, которые должны переключаться по каждому нажатию кнопки, подсоединенной к выводу INTO (раз- мещение этого вывода на корпусе для различных микроконтроллеров может отли- чаться). Реализация этой программы — в листинге 4.4. е Файлы для этого примера PushBlink. с и makefile находятся на прилагаемом к книге компакт-диске в паПке Projects\AVR\PushBlink. Листинг 4.4. Файл LEDBlink . с #include <avr/io.h> #include <avr/interrupt.h> //Для доступа к функции sei() #include <avr/signal.h> //Для доступа к макросу INTERRUPT INTERRUPT(SIG_INTERRUPT0) //Подпрограмма обработки прерывания по { //внешнему прерыванию на выводе INTO PORTB л= OxFF; //Переключаем все выводы порта В } int main (void)* .
160 Глава 4. Программные примеры для микроконтроллеров AVR Листинг 4.4. Окончание { DDRB = OxFF; //Все выводы порта В — выходы GIMSK = 0x40; MCUCR = 0x02; //Разрешаем внешнее //Устанавливаем в 1 прерывание по выводу INTO разряд ISC01, чтобы //прерывание sei(); while(1) ; } вызывалось по ниспадающему фронту сигнала на INTO //Общее разрешение прерываний //Ожидаем внешнего прерывания Прием символа от ПК через интерфейс RS-232 и передача в ответ строки с помощью UART Пусть вывод 0 порта D будет назначен в качестве линии приема RXD приемо- передатчика UART, а вывод 1 того же порта — в качестве линии передачи TXD. Для преобразования уровней последовательных сигналов, используемых микро- контроллером, к уровням, используемых интерфейсом RS-232 можно применить микросхему МАХ233 производства MAXIM (схема соединений — рис. 4.1). Микроконтроллер AVR Микросхема МАХ233 RS-232 (TXD) PD1 2 T1IN T1OUT T2IN T2OUT R1OUT R1IN 5 1 8 __ (RXD) PD0 3 1 •• oL. 1 ~ 4 ’ •• oli-i receive Рис. 4.1. Вариант схемы сопряжения микроконтроллера AVR с интерфейсом RS-232 ПК через микросхему МАХ233 Техническое описание микросхемы МАХ233 можно найти на прилагаемом к книге компакт- ’’Ж ' диске в файле Projects\DataSheets\MAX233 . pdf. Пользователь нажимает на клавиатуре ПК какую-либо цифровую клавишу, ASCII-код которой передается по последовательному интерфейсу на вход прие- мопередатчика UART. Этот код обрабатывается в соответствующей подпрограм- ме обслуживания прерывания, и на основании выполненного анализа через UART на ПК передается та или иная текстовая строка. При этом используется следующий алгоритм. 1. Инициализация UART, подразумевающая разрешение приема, передачи и пре- рывания в случае передачи символа данных. Кроме того, следует установить скорость обмена (примем ее равной 9600 бод). 2. В бесконечном цикле проверяется состояние разряда RCX регистра USR. Если он установлен в 1, то это говорит о поступлении на вход UART символа дан- ных. 3. Считывание принятого символа из регистра UDR, его анализ и последователь- ная передача на ПК соответствующей строки. На передачу всей строки может уйти довольно много времени. Для того чтобы избавить микроконтроллер от необходимости ожидать завершения передачи, можно воспользоваться очере-
Прием символа от ПК через интерфейс RS-232 и передача в ответ строки с помощью UART 161 дью символов, каждый символ из которой передается при каждом последую- щем прерывании на передачу. Очередь реализуем с помощью символьного массива. Реализация этого алгоритма представлена в листинге 4.5. . Файлы для этого примера RChrTStr. с и makefile находятся на прилагаемом к книге ком- ’"Ш.. пакт-диске в папке Proj ects\AVR\RChrTStr. Листинг 4.5. Файл RChrTStr. с #include <avr/io.h> #include <avr/interrupt.h> //Для доступа к функции sei () #include <avr/signal.h> //Для доступа к макросу INTERRUPT //Определяем одиннадцать выводимых строк (последняя - на тот //случай, если был принят не цифровой символ) char strO[] = "Zero"; char strl[] = "One"; char str2[] = "Two"; char str3[] = "Three"; char str4[] = "Four"; char str5[] = "Five"; char str6[] = "Six"; char str7[] = "Seven"; char str8[] = "Eight"; char str9[] = "Nine"; char strDefault[] = "Not digit"; unsigned char queueC, sendC; //Индексы текущего и переданного //символа unsigned char queue[50]; //Очередь INTERRUPT(SIG_UARTO_TRANS) //Подпрограмма обработки прерывания { //при завершении передачи очередного символа //Если был передан не последний символ, то передаем текущий и //увеличиваем счетчик переданных на 1 if (queueC != sendC) UDR = queue[sendC++]; } //Функция формирования очереди символов из строки void SendStr(char *s) { queueC =0; //Текущий символ - первый sendC = 1; //Первый символ считаем уже переданным queue[queueC++] = OxOD; //Добавляем в конец очереди символы queue[queueC++] = ОхОА; // возврата каретки и переноса строки //Просматриваем строку и помещаем в очередь символы while (*s) queue[queueC++] = *s++; UDR = queue[0]; //Передаем первый символ, чтобы начать процесс } 11 — 6-767
162 Глава 4. Программные примеры для микроконтроллеров AVR Листинг 4.5. Окончание at main (void) char c; //Переменная для хранения принятого от ПК символа UCR = 0x58; //Активизируем приемник, передатчик и прерывание UBRR = 0x33; //Скорость передачи = 9600 бод sei () ; //Общее разрешение прерываний while(1) //Бесконечный цикл { if (UCR & 0x80) //Если UART принял символ от ПК,... ( с = UDR; //сохраняем его в переменной с switch (с) //Анализируем символ и выводим соответствующую { //строку через UART case '01 : SendStr(strO); break; case '1' : SendStr(strl); break; case '2' : SendStr(str2); break; case ’3’ : SendStr(str3); break; case '4' : SendStr(str4); break; case '5’ : SendStr(str5); break; case '6' : SendStr(str6); break; case '7' : SendStr(str7); break; case '8 ' : SendStr(str8); break; case '9' : SendStr(str9); break; default: SendStr(strDefault); 1 1 } Светофор, управляемый напряжениями разного уровня на аналоговом входе микроконтроллера К выводам порта D подключены три светодиода с цветным покрытием. Выво- ду 0 соответствует зеленый свет, выводу 1 — желтый, а выводу 2 — красный. Управление сигналами этого “светофора” осуществляется по уровню напряжения на аналоговом входе (пусть это будет вывод 3 порта А). При напряжении до 1 В на этом входе должен загораться зеленый свет, при напряжении 1..2 В — желтый, а при напряжении свыше 2 В — красный. Значение преобразования сохраняется в регистровой паре ADCL:ADCH, ко- торой в WinAVR соответствует константа ADCW. Разрешение АЦП — 10 бит, а предельное значение напряжения на аналоговом входе составляет 5 В. Таким об- разом, для срабатывания зеленого света значение ADCW должно быть меньше, чем 1 (210 - 1) / 5 = 1023 / 5. Аналогичным образом вычисляются и пороговые вели- чины для красного (больше 2046 / 5) и желтого (1023 / 5 .. .2046 / 5) света. Реализация этой программы — в листинге 4.6. ©Файлы для этого примера Traf f icL. с и makefile находятся на прилагаемом к книге ком- пакт-диске в папке Projects\AVR\Traf ficL.
Обмен данными по интерфейсу SPI 163 Листинг 4.6. Файл Traf f icL. с #include <avr/io.h> #include <avr/interrupt.h> //Для доступа к функции sei() #include <avr/signal.h> //Для доступа к макросу INTERRUPT INTERRUPT(SIG_ADC) //Обработчик прерывания от интегрированного АЦП { unsigned int ADCdata; //Буферная переменная для хранения ADCdata = ADCW; //результата преобразования if (ADCdata > (2046 / 5)) PORTD = 3; //Если U > 2 В, то //PORTD = 011 ("красный") else if (ADCdata < (1023 /5)) PORTD = 6; //Если U < 1 В, то //PORTD = 110 ("зеленый") else PORTD = 5; //Если U = 1..2 B, PORTD = 101 ("желтый") ADCSR = ADCSR | 0x40; //Устанавливаем разряд ADSC в регистре //ADCSR, чтобы начать новое преобразование } int main (void) { DDRD =7; //Три младших разряда порта D - выходы ADMUX = 3; //Назначаем в качестве аналогового входа РАЗ ADCSR = ОхСЕ; /* 0Ы1001110 - активизируем АЦП с коэффициентом деления 64, разрешаем прерывание от АЦП и начинаем преобразование */ sei(); //Общее разрешение прерываний while(1) ; //Бесконечный цикл в ожидании прерывания от АЦП } Обмен данными по интерфейсу SPI К выводам порта D подключены переключатели, а к выводам порта С - свето- диоды. Значение, выбранное с помощью переключателей выводится по интерфей- су SPI, а принятые данные отображаются с помощью светоиндикации. Реализация этой программы — в листинге 4.7. -------------------------------------------------------------- Файлы для этого примера SPIInOut. с и makefile находятся на прилагаемом к книге ком- пакт-диске в папке Projects\AVR\SPIInOut. Листинг4.7. Файл SPIInOut.с #include <avr/io.h> #include <avr/interrupt.h> //Для доступа к функции sei() #include <avr/signal.h> //Для доступа к макросу INTERRUPT INTERRUPT(SIG_SPI) //Обработчик прерывания от SPI { PORTC = SPDR; //Отображаем с помощью светодиодов принятые //данные SPDR = PIND; //Загружаем новые данные, чтобы начать передачу 11*
164 Глава 4. Программные примеры для микроконтроллеров AVR Листинг 4.7. Окончание } int main (void) { PORTB = 0x40; //Вывод 6 порта В - MISO (с подтягивающим //сопротивлением) DDRB = ОхВО; //Выходы SPI: вывод 7 - SCK, вывод 5 - MOSI, DDRC = PORTD = SPCR = //вывод 4 - SS OxFF; //Все выводы порта С - выходы ; OxFF; //Все выводы порта D - с подтягивающим //сопротивлением 0xD0; //0Ы1010000 - активизируем SPI в режиме "Master" //и разрешаем прерывания от SPI sei(); //Общее разрешение прерываний SPDR = 0x00; //Начинаем передачу (вызов прерывания) while(1) ; //Бесконечный цикл в ожидании прерывания от SPI Управление яркостью свечения светодиода с помощью широтно-импульсной модуляции Когда на вывод некоторого порта с постоянной частотой подается и снимает- ся напряжение, получается последовательность импульсов, подобная импульсам системной синхронизации. Обычно при равномерных импульсах первую полови- ну такта на выводе присутствует напряжение, а вторую — отсутствует. Такое рас- пределение называется 50%-м рабочим циклом (рис. 4.2). Мы можем варьировать распределением нагрузки от 0% рабочего цикла (напряжение постоянно отсутст- вует) до 100% (напряжение постоянно подается). 50% рабочий цикл 75% рабочий цикл 25% рабочий цикл ---------- ------------- Рис. 4.2. Рабочий цикл при широтно-импульсной модуляции Если сохранить частоту импульсов неизменной, но при этом снижать или уве- личивать время использования светодиода за один такт, тем самым можно управ- лять мощностью, потребляемой светодиодом, и, в конечном итоге, — яркостью его свечения. Человеческий глаз воспринимает мигающие с большой частотой светодиоды так, как будто они постоянно включены. Кроме того, мы воспринима- ем яркость быстро пульсирующего света как промежуточную между пиковой и средней. Это означает, что интенсивные импульсы с низким рабочим циклом вы- глядят ярче. Благодаря таким особенностям восприятия можно обеспечить свече-
Управление яркостью свечения светодиода с помощью широтно-импульсной модуляции 165 ние, которое кажется более ярким, задействовав широтно-импульсную модуляцию (ШИМ). К примеру, в случае таймера/счетчика Т/С 1 для перехода в режим ШИМ сле- дует установить разряды PWM10 и PWM11 регистра TCCR1A. В этом режиме Т/Cl работает как суммирующий и вычитающий счетчик, осуществляя цикличе- ские переходы от 0x0000 к максимальному значению и затем снова возвращаясь к 0x0000. Когда значение счетного регистра совпадает со значением регистра OCR1A, то на выводе микроконтроллера ОС1А устанавливается высокий или низ- кий уровень сигнала (определяется состоянием разрядов СОМ1А1 и СОМ1А0 ре- гистра TCCR1 А). Таким образом, если к выводу ОС1А подсоединить светодиод, то в режиме ШИМ “интенсивностью” его свечения можно управлять, изменяя значение в реги- стре сравнения OCR1. Реализация циклического увеличения и уменьшения “интенсивности” свече- ния светодиода, подсоединенного к выводу ОС1А, представлена в листинге 4.8. В этом примере продемонстрировано использование директив препроцессора #if, #elif и #endif, а также библиотечного макроса _BV, используемого для уста- новки того или иного разряда в лог. 1. ©Файлы для этого примера LED_PWM. с и makefile находятся на прилагаемом к книге ком- пакт-диске в папке Projects\AVR\LED_PWM. Листинг 4.8. Файл led pwm.c #include <avr/io.h> #include <avr/interrupt.h> //Для доступа к функции sei() #include <avr/signal.h> //Для доступа к макросу INTERRUPT /* Определяем константы для вывода ОС1, его порта и регистра сравнения, используемых в режиме ШИМ, в зависимости от типа микроконтроллера, для которого была скомпилирована программа */ # if defined( AVR AT90S2313___) # define OC1 PB3 # define OCR OCR1 # define DDROC DDRB #elif defined ( AVR AT90S2333__) | | defined ( AVR AT90S4433_) # define OC1 PB1 # define DDROC DDRB # define OCR OCR1 #elif defined( AVR AT90S4414_) | |defined( AVR AT90S8515___)|| \ defined( AVR AT90S4434_)||defined( AVR AT90S8535___)|| \ defined (__AVR_ATmegal63_) # define OC1 PD5 # define DDROC DDRD # define OCR OCR1A #endif /* Определяем константу, соответствующую разряду СОМ1А регистра TCCR1A, который задает характер сигнала на выводе ОС1 при совпадении содержимого счетчика с регистром сравнения */
166 Глава 4. Программные примеры для микроконтроллеров AVR Листинг 4.8. Окончание # if defined(СОМ11) # define XCOM11 COM11 # elif defined(COM1A1) # define XCOM11 C0M1A1 #endif enum { UP, DOWN }; //Перечислимый тип: UP = 0, DOWN = 1 unsigned int pwm; //Значение, записываемое в регистр сравнения unsigned char direction; //Определяет направление изменения pwm INTERRUPT(SIG_OVERFLOW1) //Прерывание при достижении счетчиком { //значения 0 ./* Если pwm изменяется в сторону увеличения, то увеличиваем его на 1, в противном случае - уменьшаем на 1. Если при этом достигнуто пороговое значение 1023 или 0, то направление изменения pwm меняется на противоположное */ switch (direction) { case UP: if (++pwm == 1023) direction = DOWN; break; case DOWN: if (--pwm == 0) direction = UP; break; } OCR = pwm; //Записываем значение ШИМ в регистр сравнения } void main (void) { //Устанавливаем 10-разрядную, неинвертирующую ШИМ, //используется регистр сравнения А TCCR1A = _BV(PWM10) | -BV(PWMll) | _BV(XCOM11) ; TCCR1B — _BV(CS10); //Делитель частоты не используется TIMSK = _BV(TOIE1); //Разрешаем прерывание от Т/С1 OCR = 0; //Начальное значение ШИМ DDROC = _BV(OC1); //Определяем вывод 0С1 как выход sei(); //Общее разрешение прерываний while(l); //Бесконечный цикл в ожидании прерывания от Т/С1 } В данном примере мы использовали для управления состоянием вывода ОС1А прерывание, возникающее при достижении счетчика нулевого состояния. Управление интенсивностью свечения светодиода можно также реализовать, вы- бирая один из типов рабочего цикла ШИМ с помощью трех переключателей, под- ключенных к выводам 0-2 порта С. Так, если все переключатели выключены, то это соответствует 10% рабочего цикла, если включен переключатель на выводе 0 — 20%, переключатель на выводе 1 — 30%, переключатели на выводах 0 и 1 — 40% и т.д. Если все переключатели включены, то это соответствует циклу 80%. Реализация программы представлена в листинге 4.9.
Управление яркостью свечения светодиода с помощью широтно-импульсной модуляции 167 Файлы для этого примера LED_Duty. с и makefile находятся на прилагаемом к книге ком- Wfc пакт-диске в папке Projects\AVR\LED_Duty. Листинг4.9. Файл LED_Duty.с #include <avr/io.h> # if defined( AVR AT90S2313___) # define OC1 PB3 # define OCR OCR1 # define DDROC DDRB #elif defined( AVR AT90S2333_____) | |defined( AVR AT90S4433__) # define OC1 PB1 # define DDROC DDRB # define OCR OCR1 #elif defined( AVR AT90S4414___)||defined( AVR AT90S8515___)|| \ defined( AVR AT90S4434___) | |defined( AVR AT90S8535__) | | \ defined( AVR ATmegal63_____) # define OC1 PD5 # define DDROC DDRD # define OCR OCR1A #endif # if defined(COMI1) # define XCOM11 COM11 # elif defined(COM1A1) # define XCOM11 COM1A1 #endif //PWM_DUTY соответствует текущее значение на выводах 0-2 порта С #define PWMJDUTY (PINC & 7) void main (void) { PORTC = 0x07; //Выводы 0-2 порта С - входы с внутренним //подтягивающим сопротивлением //Устанавливаем 8-разрядную, неинвертирующую ШИМ, //используется регистр сравнения А TCCR1A = _BV(XCOM11) | _BV(PWM10) ; TCCR1B = _BV(CS10); //Делитель частоты не используется DDROC = _BV(OC1); //Определяем вывод ОС1 как выход while (1) { /* Постоянно обновляем содержимое регистра сравнения на основании текущего состояния выводов 0-2 порта С. Если все переключатели выключены, то в регистр OCR записывается число 25, что соответствует 10% рабочего цикла ШИМ. Если все переключатели включены, то записывается число 204, что соответствует 80% рабочего цикла ШИМ */ OCR = (PWM_DUTY + 1) * 2550 / 100; } }
168 Глава 4. Программные примеры для микроконтроллеров AVR - Измерение ширины импульсов На вывод ICP (Input Capture Pint — вывод захвата на входе) микроконтролле- ра подаются импульсы, ширина которых измеряется и ее значение в миллисекун- дах отображается с помощью светодиодов, подключенных к выводам порта С. Пример для микроконтроллера AT90S8535 представлен в листинге 4.10. ©Файлы для этого примера PulsWdth. с и makefile находятся на прилагаемом к книге ком- пакт-диске в папке Projects\AVR\PulsWdth. Листинг 4.10. Файл PulsWdth. с #include <avr/io.h> #include <avr/interrupt.h> //Для доступа к функции sei() #include <avr/signal.h> //Для доступа к макросу INTERRUPT unsigned char OverflowC; //Счетчик переполнений для Т/Cl unsigned int EdgeR, EdgeF; //Для сохранения времени появления //нарастающего и ниспадающего фронтов импульса unsigned long PulseClocks; //Количество тактовых импульсов, //помещающихся внутри импульса на входе ICP INTERRUPT(SIG_OVERFLOW1) //Прерыв ание при переполнении Т/С1 { OverflowC++; //Увеличиываем счетчик переполнений } INTERRUPT(SIG_INPUT_CAPTURE1 ) //Прерывание при захвате на входе { if ((PIND & 0x40) ’= 0) //Если на выводе 6 порта D - высокий { //уровень,... EdgeR = ICR1; //Время возникновения нарастающего фронта TCCR1B = TCCR1B & OxBF; //Устанавливаем следующий захват по //ниспадающему фронту сигнала на ICP OverflowC = 0; //Сбрасываем счетчик переполнений } else // Если на выводе 6 порта D - низкий уровень { EdgeF а= ICR1; //Время возникновения ниспадающего фронта TCCR1B = TCCR1B | 0x40; //устанавливаем следующий захват по //нарастающему фронту сигнала на ICP //Вычисляем количество тактовых импульсов внутри импульса на ICP PulseClocks = (unsigned long)EdgeF - (unsigned long)EdgeR + (unsigned long)OverflowC * 0x10000; PORTC = PulseClocks / 500; //Отображаем значение в миллисек. } } int main (void) {
Измерение скорости вращения двигателя с отображением результата на ЖК-дисплее 169 Листинг 4.10. Окончание DDRC = OxFF; TCCR1B = 0хС2; //Все выводы порта С - выходы //Коэффициент деления для входного тактового //сигнала Т/Cl = 8; разрешен захват на входе по //нарастающему фронту сигнала на выводе ICP TIMSK = 0x24; //Снимаем маску с прерываний при переполнении и //захвате для Т/С1 sei(); //Общее разрешение прерываний while(1) ; I //Бесконечный цикл в ожидании прерываний от Т/С1 Измерение скорости вращения двигателя с отображением результата на ЖК-дисплее Для измерения скорости вращения можно воспользоваться оптопрерывате- лем — прибором, в котором между эмитирующим инфракрасное излучение дио- дом и транзистором-детектором имеется воздушная прослойка (например, прибор Н21А1 производства FAIRCHILD представлен на рис. 4.3). Рис. 4.3. Оптопрерыватель Н21А1 ©Техническое описание прибора Н21А1 можно найти на прилагаемом к книге компакт-диске в файле Projects\DataSheets\H21Al.pdf. Если между диодом и детектором поместить непрозрачный объект, транзи- стор разомкнет цепь и тем самым “прервет” сигнал. Подключив такое устройство к выводу ICP микроконтроллера, можно в режиме ШИМ регистрировать частоту вращения диска с вырезом, закрепленного на оси двигателя. Всякий раз, когда вырез будет находиться в щели оптопрерывателя, транзистор будет замыкать цепь, а как только вырез будет уходить из щели, транзистор будет ее размыкать. Если выполнять подсчет раз в секунду, то будет получено количество оборотов вала в 1 с, то есть, частоту вращения в герцах. Используем микроконтроллер с тактовой частотой 8 МГц (например, AT90S8535). Используя коэффициент деления 8 получаем частоту тактирования Т/Cl равной 1 МГц (период такта — 1 мкс). Каждый раз, когда на выводе ICP
170 Глава 4. Программные примеры для микроконтроллеров AVR микроконтроллера (для устройства AT90S8535 — вывод PD6) появляется ниспа- дающий фронт сигнала, возникает прерывание по захвату на входе, и вычисляется количество тактовых импульсов между предыдущим и текущим моментом захвата (хранится в регистре ICR1). На основании этого значения вычисляется скорость вращения вала двигателя (в оборотах в минуту) по формуле: U = 60-106/T, где Т — длительность одного оборота в мкс (1 секунда = 106 мкс). Полученный результат будем отображать на ЖК-дисплее. Выберем, к приме- ру, для этой цели ЖК-модуль DMC2048 производства OPTREX. Техническое описание модуля DMC2048 можно найти на прилагаемом к книге компакт-диске ч». в файле Proj ects\DataSheets\DMC2048 . pdf. Модуль DMC2048 работает в четырехразрядном режиме. Это означает, что все поступающие на устройство данные передаются с помощью четырех линий данных (в случае рассматриваемой микросхемы они обозначены как DB4-DB7). Используется три управляющих сигнала: Е — стробирующий сигнал для подтверждения передачи данных, предна- значенных к отображению на дисплее; RD (R/W) — определяет текущую операцию: чтение данных из ЖК-модуля (RD=1) или запись данных в ЖК-модуль (RD=0); RS — определяет характер передаваемых данных: команда (RS=0), влияю- щая на режим работы дисплея, или данные (RS=1), которые следует ото- бразить. В нашем примере линия Е ЖК-модуля будет подсоединена к выводу 0 порта В микроконтроллера, линия RS — к выводу 1, а линия RD — к выводу 2 того же порта. Линии данных будут соединены с выводами 0-3 порта С микроконтролле- ра. Реализация этого алгоритма представлена в листинге 4.11. Файлы для этого примера Speedmtr. с и makefile находятся на прилагаемом к книге ком- пакт-диске в папке Projects\AVR\Speedmtr. Листинг 4.11. Файл Speedmtr. с ttinclude <avr/io.h> #include <avr/interrupt.h> //Для доступа к функции sei() #include <avr/signal.h> //Для доступа к макросу INTERRUPT #include <avr/delay.h> //Для доступа к функции _delay_loop_2 #include <string.h> //Для доступа к функции sprintf //Определяем макросы для установки и сброса выводов порта В, //используемых в качестве линий управления ЖК-модулем: //РВО - Е; РВ1 - RS; РВ2 - RD #define RD_1 PORTB |= 4 //PORTB = PORTB | OblOO - RD = 1 #define RS_1 PORTB |= 2 //PORTB = PORTB | ObOlO - RS = 1 ttdefine E_1 PORTB |= 1 //PORTB = PORTB | ObOOl - E = 1 #define RD 0 PORTB &= 3 //PORTB = PORTB & ObOll - RD = 0
Измерение скорости вращения двигателя с отображением результата на ЖК-дисплее 171 Листинг 4.11. Продолжение #define RS_O PORTB &= 5 //PORTB = PORTB & 0Ы01 - RS = 0 #define E_0 PORTB &- 6 //PORTB = PORTB &0Ы10-Е = 0 //Объявляем массив с адресами во внутренней памяти ЖК-модуля, //соответствующими первой позиции в каждой из 4-х строк дисплея const unsigned char addLUT[4] = {0x80, OxCO, 0x94, 0xD4); unsigned char LCD_Address, LCD_Line; //Переменные для хранения //адреса позиции и номера строки ЖК-дисплея unsigned int PreviousTime; //Хранит замер времени во время //предыдущего импульса на входе ICP char buffer[15]; //Буфер для хранения строки, выводимой на дисплей //Функция записи в ЖК-модуль полубайта void WriteNibble(unsigned char data) { RD_0; //Режим записи Е_1; //Активизируем передачу PORTC = (data & OxOF); //Передаем данные Е__0; //Завершаем передачу RD_1; //Режим чтения _delay_loop_2(3000); //Задержка 3 мс, чтобы ЖК-модуль имел //достаточно времени на обработку данных } //Функция записи в ЖК-модуль байта void WriteByte(unsigned char data) { RD_0; //Режим записи Е_1; //Активизируем передачу PORTC = (data » 4); //Передаем старший полубайт Е_0; //Завершаем передачу Е_1; //Активизируем передачу PORTC = (data & OxF); //Передаем младший полубайт Е_0; //Завершаем передачу RD_1; //Режим чтения _delay_loop_2(3000); //Задержка 3 мс, чтобы ЖК-модуль имел //достаточно времени на обработку данных } //Функция перехода к первой позиции строки с номером LineNum void GoToLine(char LineNum) {- RS_0; //Передача команды LCD_Address = addLUT[LineNum-1]; //Определяем адрес строки WriteByte(LCD_Address); //и передаем его в ЖК-модуль RS_1; //Конец передачи команд LCD_Address =0; //Обнуляем адрес LCD_Line = LineNum; //Устанавливаем текущую строку }
172 Глава 4. Программные примеры для микроконтроллеров AVR Листинг 4.11. Продолжение /* Функция очистки дисплея (в программе не используется, дана только в качестве примера, если читатель пожелает расширить функциональность) */ void ClearLCD(void) { RS_0; //Передача команды WriteByte(0x01); //Команда очистки,- курсор перемещается //в исходную позицию _delay 1оор_2(10000); //Задержка 10 мс, чтобы ЖК-модуль имел //достаточно времени на обработку команды RS_1; //Конец передачи команд GoToLine(1); //Переходим к строке 1 } //Функция установки текущей позиции col в строке row дисплея void SetLCDPosition(char row, char col) { RS_0; //Передача команды LCD_Address = addLUT[row-1] + col; //Определяем позицию дисплея WriteByte(LCD_Address); //Записываем в ЖК-модуль адрес RS_1; //Конец передачи команды LCD_Line = row; //Устанавливаем текущую строку } //Функция отображения в текущей позиции на дисплее символа void ShowChar(unsigned char с) { RS—l; //Передача данных WriteByte(с); //Записываем символ LCD_Address++; //Увеличиваем адрес на 1 switch (LCD_Address) //Определяем, не произошел ли { //переход на новую строку дисплея case 20: GoToLine(2); break; case 40: GoToLine(3); break; case 60: GoToLine(4); break; case 80: GoToLine(1); break; } } //Функция вывода на дисплей строки void ShowStr(unsigned char *s) { while (*s ?= 0) ShowChar(*s++); } //Функция инициализации ЖК-модуля void InitLCD(void) { RD 1; //Предустановка управляющих сигналов
Измерение скорости вращения двигателя с отображением результата на ЖК-дисплее 173 Листинг 4.11. Продолжение Е_0; - RS_O; //Передача команд _delay__loop_2 (50000) ; //Задержка на 50 мс, чтобы ЖК-модуль //имел достаточно времени на инициализацию WriteNibble(0x33); //Передаем последовательность, WriteNibble(0x33); //переводящую ЖК-модуль в режим передачи WriteNibble(0x33); // по 4-м линиям данных (см. тех. описание) WriteNibble(0x22); WriteByte(0x28); //Активизируем внутренний шрифт (матрица 5x7) WriteByte(0x01); WriteByte(0x10); //Устанавливаем режим смещения курсора WriteByte(0x06); //вправо, без сдвига содержимого дисплея WriteByte(ОхОС); //Включаем дисплей, прячем курсор for (char i=0x40; i<0x5F; i++) //Инициализируем память { //знакогенератора _delay_loop_2(10000); RS_0; WriteByte(i); _delay_loop_2(10000); ShowChar(0) ; } RS__1; //Переходим в режим передачи данных SetLCDPosition(1, 4); //Переходим в первую строку дисплея buffer[0] = 'V; //Формируем строку "Velosity is" buffer[1] = 'е'; buffer[2] = '1'; buffer[3] = 'o'; buffer[4] = 's'; buffer[5] = 'i'; buffer[6] = 't'; buffer[7] = 'y'; buf f er[8 ] = ' '; buffer[9] = 'i'; buffer[10] = 's'; buffer[ll] = '\0'; ShowStr(buffer); //Выводим "Velosity is" на дисплей } //Прерывание по захвату на входе ICP INTERRUPT(SIG_INPUT_CAPTURE1) { unsigned int CurrentTime, T; //Вычисляем текущее значение времени замера CurrentTime = (256 * ICR1H) + ICR1L; //Проверяем, было ли переполнение значения. //Если нет, то определяем период Т со времени предыдущего замера if (CurrentTime > PreviousTime) Т = CurrentTime - PreviousTime; //Если было переполнение, то определяем период Т с коррекцией
174 Глава 4. Программные примеры для микроконтроллеров AVR Листинг 4.11. Окончание else Т = OxFFFF - CurrentTime + PreviousTime; //Сохраняем в буфер buffer форматированную строку, содержащую //значение скорости вращения вала двигателя (rpm - об./мин.) sprintf(buffer, "%06u rpm", (unsigned long)60E6 / (unsigned long)T ); SetLCDPosition(2, 4); //Переходим во вторую строку дисплея ShowStr(buffer); //Отображаем значение скорости PreviousTime = CurrentTime; //Делаем текущий замер "предыдущим" } void main (void) { DDRC = OxOF; //Выводы 0-4 порта С - выходы (передача данных) DDRB = 0x07; //Выводы 0-3 порта //сигналы) В - выходы (управляющие PORTD = _BV(PD6) DDRD = 0; ; //Вывод РОб - вход для сигнала ICP TIMSK = _BV(TICIE1) ; //Разрешаем прерывание при захвате на входе TCCR1A = 0; //Режим ШИМ для Т/Cl отключен TCCR1B = BV(CSll); //Коэффициент деления частоты системной //синхронизации = 8. Захват по ниспадающему фронту TCNT1 =0; //Начинаем счет с 0 InitLCDO; //Инициализируем ЖК-дисплей sei (); //Общее разрешение прерываний while(l); //Бесконечный цикл в ожидании сигнала на выводе ICP } Контроль скорости вращения вентилятора с учетом показаний датчика температуры Самый простой способ замера температуры — с помощью термистора (тер- морезистора). Такие устройства выпускаются для широкого диапазона температур и, при правильном подборе, не требуют специального усиления или предвари- тельной обработки сигналов. Существует также и множество других температурных сенсоров, включая би- металлические термопары и резистивные температурные устройства (RTD, Resis- tive Temperature Device). Стоит также упомянуть о специальных микросхемах, на- подобие LM35 от компании National Semiconductor, — калиброванные, стабиль- ные в измерениях устройства, выдающие определенный уровень напряжения на градус Цельсия. Например, для LM35 выходное значение составляет 10 мВ/°С. Тем не менее, несмотря на все свои достоинства, подобные устройства более до- рогостоящие и требуют дополнительных схем. Техническое описание микросхемы LM35 можно найти на прилагаемом к книге компакт-диске в файлеРгоj ects\DataSheets\LM35. pdf.
Контроль скорости вращения вентилятора с учетом показаний датчика температуры 175 Для опроса температуры в нашей программе выбе- +5 рем термистор с отрицательным температурным коэф- а фициентом (NTC, Negative Temperature Coefficient). Поскольку термистор выдает значения в виде процен- тов сопротивления на градусы Цельсия, для измерения (t< ] температуры можно воспользоваться резистивным де- Выход лителем. Диапазон выходных напряжений делителя Г также можно приспособить к диапазону АЦП, обеспе- <47 кОм чив таким образом наилучшее разрешение. < В микроконтроллерах AVR ко входу АЦП обычно | подключают цепи с небольшим выходным импедансом (ОКОЛО 5 кОм ИЛИ ниже). Выберем резистор на 4,7 кОм Рис. 4.4. Резистивный дели- для формирования делителя с термистором 2 кОм тель с термистором (приблизительное сопротивление при 25°С) (рис. 4.4). Выбранный термистор изменяет величину на 3,83% на каждый 1°С. Это обес- печивает диапазон напряжения в АЦП от 0,84 В при -40°С до 4,5 В при 61 °C. Термистор размещен в верхнем плече делителя, поскольку его отрицательный им- педанс изменяется с температурой. В результате с повышением температуры бу- дет повышаться и напряжение на входе АЦП. Разрешение АЦП составляет 10 разрядов, поэтому измеренные значения тем- пературы соответствуют следующим пределам: -40°С = 0x000 =0; 61°С = 0x3FF= 1023. Отсюда получаем формулу для вычисления текущей температуры: t=101°C- ADCW / 1023 - 40°С, где ADCW — текущее содержимое регистра данных интегрированного АЦП. В нашем примере в качестве аналогового входа, к которому подключен выход резистивного делителя с термистором, будем использовать вывод 3 порта А. Для приведения в действие вентилятора охлаждения воспользуемся, к примеру, элек- тродвигателем на 9 В, пример схемы подключения которого представлен на рис. 4.5. Рис. 4.5. Пример схемы подключения электродвигателя к микроконтроллеру
176 Глава 4. Программные примеры для микроконтроллеров AVR Исходя из этой схемы, управлять скоростью вращения можно в режиме ШИМ Т/Cl. Напомним, что в этом режиме Т/Cl работает как суммирующий и вычи- тающий счетчик, осуществляя циклические переходы от 0x000 0 к максимальному значению и затем снова возвращаясь к 0x0000. Когда значение счетного регистра совпадает со значением регистра OCR1A, то на выводе микроконтроллера ОС1А устанавливается высокий или низкий уровень сигнала (определяется состоянием разрядов СОМ1А1 и СОМ 1 АО регистра TCCRIA). Таким образом, чем меньше значение хранится в регистре сравнения OCR1A, тем реже поступают управляю- щие импульсы, включающие двигатель. Примем, что температуре 30°С и ниже двигатель вообще отключен (OCR1A = 0), а при температуре 60°С — вращается с максимальной скоростью (OCR1A = 1022). Отсюда, формула для определения текущего значения регистра OCR1А в зависимости от температуры имеет вид: OCR1А = (t - 30) 1022 / 30°С. Данные о текущей температуре и относительной скорости вращения вентиля- тора будут отображены на ЖК-дисплее (выберем для этой цели модуль DMC2048 рассмотренный в предыдущей главе). Реализация программы (для AT90S8535) представлена в листинге. 4.12. Файлы для этого примера TempCtrl. с и makefile находятся на прилагаемом к книге ком- • пакт-диске в папке Projects\AVR\TempCtrl. Листинг 4.12. Файл TempCtrl. с #include <avr/io.h> #include <avr/interrupt.h> //Для доступа к функции sei() #include <avr/signal.h> //Для доступа к макросу INTERRUPT #include <avr/delay.h> //Для доступа к функции _delay_loop_2 #include <string.h> //Для доступа к функции sprintf /* Определяем макросы для установки и сброса выводов порта В, используемых в качестве линий управления ЖК-модулем: PBO - E; PB1 - ] PS; PB2 - RD * / #define RD_ 1 PORTB 1 = 4 //PORTB = PORTB 1 OblOO - RD = 1 #define RS_ 1 PORTB 1 = 2 //PORTB = PORTB 1 ObOlO - RS = 1 #define E 1 PORTB 1 = 1 //PORTB = PORTB | ObOOl - E = 1 #define RD_ 0 PORTB &= 3 //PORTB = PORTB & ObOll - RD = 0 #define RS_ 0 PORTB &= 5 //PORTB = PORTB & 0Ы01 - RS = 0 #define E_0 PORTB &= 6 //PORTB = PORTB & 0Ы10 - E = 0 //Объявляем массив с адресами во внутренней памяти ЖК-модуля, //соответствующими первой позиции в каждой из 4-х строк дисплея const unsigned char addLUT[4] = {0x80, 0xC0, 0x94, 0xD4}; unsigned char LCD_Address, LCD_Line; //Переменные для хранения //адреса позиции и номера строки ЖК-дисплея char buffer[15]; //Буфер для хранения строки, выводимой на ЖК- -дисплей //Функция записи в ЖК-модуль полубайта void WriteNibble(unsigned char data)
Контроль скорости вращения вентилятора с учетом показаний датчика температуры 177 Листинг 4.12. Продолжение RD_0; //Режим записи Е_1; //Активизируем передачу PORTC = (data & OxOF); //Передаем данные Е_0; //Завершаем передачу RD_1; //Режим чтения _delay_loop_2(3000); //Задержка 3 мс, чтобы ЖК-модуль имел //достаточно времени на обработку данных //Функция записи в ЖК-модуль байта void WriteByte(unsigned char data) { RD_0; //Режим записи Е_1; //Активизируем передачу PORTC = (data » 4); //Передаем старший полубайт Е__0; //Завершаем передачу Е__1; //Активизируем передачу PORTC = (data & OxF); //Передаем младший полубайт Е_0; //Завершаем передачу RD_1; //Режим чтения _delay_loop_2(3000); //Задержка 3 мс, чтобы ЖК-модуль имел //достаточно времени на обработку данных } //Функция перехода к первой позиции строки с номером LineNum void GoToLine(char LineNum) { RS_0; //Передача команды LCD_Address = addLUT[LineNum-1]; //Определяем адрес строки WriteByte(LCD_Address); //и передаем его в ЖК-модуль RS_1; //Конец передачи команд LCD_Address = 0; //Обнуляем адрес LCD_Line = LineNum; //Устанавливаем текущую строку } //Функция установки текущей позиции col в строке row дисплея void SetLCDPosition(char row, char col) { RS__0; //Передача команды LCD_Address = addLUT[row-1] + col; //Определяем позицию дисплея WriteByte(LCD_Address); //Записываем в ЖК-модуль адрес RS_1; //Конец передачи команды LCD_Line = row; //Устанавливаем текущую строку } //Функция отображения в текущей позиции на дисплее символа void ShowChar(unsigned char с) { RS 1; //Передача данных 12-6-767
178 Глава 4. Программные примеры для микроконтроллеров AVR Листинг 4.12, Продолжение WriteByte(с); //Записываем символ LCD Address++; //Увеличиваем адрес на 1 switch (LCD Address) //Определяем, не произошел ли { //переход на новую строку дисплея case 20: GoToLine(2); break; case 40: GoToLine(3); break; case 60: GoToLine(4); break; case 80: GoToLine(l); break; } } void ShowStr(unsigned char *s) //Функция вывода на дисплей строки { while (*s •= 0) ShowChar(*s++); } //Функция инициализации ЖК-модуля void InitLCD(void) { unsigned char titlel[] = "Temperature: "; //Строки заголовков unsigned char title2[] = "Motor velocity: "; //для вывода на //ЖК-дисплей RD_1; //Предустановка управляющих сигналов Е_0; RS_0; //Передача команд _delay_loop_2(50000) ; //Задержка на 50 мс, чтобы ЖК-модуль //имел достаточно времени на инициализацию WriteNibble(0x33); //Передаем последовательность, WriteNibble(0x33); //переводящую ЖК-модуль в режим передачи WriteNibble(0x33); //по 4-м линиям данных (см. тех. описание) WriteNibble(0x22); WriteByte(0x28); //Активизируем внутренний шрифт (матрица 5x7) WriteByte(0x01); WriteByte(0x10); //Устанавливаем режим смещения курсора WriteByte(0x06); //вправо, без сдвига содержимого дисплея WriteByte(ОхОС); //Включаем дисплей, прячем курсор for (char i=0x40; i<0x5F; i++) //Инициализируем память { //знакогенератора _delay_loop_2(10000); RS__0 ; WriteByte(i); _delay_loop_2(10000); ShowChar(0); } RS_1; //Переходим в режим передачи данных SetLCDPosition(1, 4); //Переходим в первую строку дисплея ShowStr(titlel); //Выводим "Temperature:" SetLCDPosition(3, 2); //Переходим в третью строку дисплея ShowStr(title?); //Выводим "Motor velocity:"
Контроль скорости вращения вентилятора с учетом показаний датчика температуры 179 Листинг 4.12. Окончание //Прерывание по завершению АЦП-преобразования INTERRUPT (SIG_ADC) { unsigned char CurrentTemp, CurrentVel; //Вычисляем текущую температуру на основании регистра ADCW CurrentTemp = ((long)101 * (long)ADCW) / (long) 1023 - (long)40; //Вычисляем значение для записи в регистр OCR1A CurrentVel = (CurrentTemp - 30) * 1022 / 30; //Формируем строку температуры sprintf(buffer, ”%02d С", CurrentTemp); SetLCDPosition(2, 8); //и выводим ее на ЖК-дисплей ShowStr(buffer); sprintf(buffer, ”%04d", CurrentVel); //Формируем строку скорости SetLCDPosition(4, 8); //и выводим ее на ЖК-дисплей ShowStr(buffer); OCR1A = CurrentVel; //Изменяем скорость вращения двигателя //вентилятора } void main (void) { DDRA = 0; //Выводы порта А - входы DDRB = 0x07; //Выводы 0-3 порта В - выходы (управляющие сигналы) DDRC = ОхОЕ; //Выводы 0-4 порта С - выходы (передача данных) DDRD = _BV(PD5); //Вывод 5 порта D — выход ОС1А в режиме ШИМ TIMSK = _BV(TOIE1); //Разрешаем прерывание при переполнении Т/С1 //Устанавливаем 10-разрядную, неинвертирующую ШИМ, //используется регистр сравнения А TCCR1A = _BV(PWM10) | _BV(PWM11) | _BV(COM1A1) ; TCCR1B = _BV(CS10); //Делитель частоты не используется TCNT1 = 0; //Начинаем счет с 0 OCR1A = 0; //Начальное значение для режима ШИМ ACSR = _BV(ACD); //Отключаем аналоговый компаратор ADMUX = 3; //Выбираем аналоговый канал 3, вывод AREF - //вход для опорного напряжения /* Активизируем АЦП в несинхронизированном режиме, деление частоты на 64, чтобы при частоте системной синхронизации 8МГц получить частоту тактирования АЦП 125 кГц (это значение должно всегда нахдиться в пределах (50 - 200 кГц) */ ADCSR = _BV(ADEN) | _BV(ADSC) | _BV(ADFR) | _ BV(ADIE) | _BV(ADPS2) | _BV(ADPS1) ; InitLCD(); ’ //Инициализируем ЖК-дисплей sei (); //Общее разрешение прерываний while(1); //Б есконечный цикл } 12*
180 Глава 4. Программные примеры для микроконтроллеров AVR Проект “Часы реального времени” В завершение этой главы рассмотрим проект, реализующий с помощью мик- роконтроллера ATmegal69 часы реального времени, используя при этом обмен данными с ПК через приемопередатчик UART. Функция калибрования генератора синхроимпульсов Если попытаться измерять время с помощь некалиброванного генератора им- пульсов, встроенного в микроконтроллер, то полученные таким образом часы бу- дут в день спешить или отставать на пару часов. И дело даже не в самом генерато- ре — он всегда создает очень точные импульсы, однако, поскольку в процессе производства нельзя получить двух абсолютно идентичных генераторов, частота этих импульсов у разных микроконтроллеров имеет допуск в некотором диапазо- не, что и проявляется в несовпадении с “реальным” временем. Часы реального времени устанавливаются национальными организациями по стандартизации с помощью атомных часов. Для калибровки встроенного генера- тора импульсов в соответствии с точными показаниями времени необходимо ис- пользовать какую-то внешнюю микросхему, частота импульсов которой точно синхронизирована с “атомными часами”. Для калибровки генератора синхроим- пульсов, работающего с частотой 8 МГц, можно, например, воспользоваться внешней тактирующей микросхемой с частотой 32768 кГц. Подобные микросхе- мы “часов” характеризуются высокой точностью и дешевизной. е Техническое описание одной из таких микросхем: Е1217Х от компании ATMEL — можно найти на прилагаемом к книге компакт-диске в файле Projects\DataSheets\E1217X. pdf. Итак, для точного замера длительности некоторого периода времени следует подсчитывать импульсы, генерируемые микросхемой “часов”. Например, если ко- личество импульсов достигнет значения 32768, то мы точно знаем, что прошла одна секунда. На практике для калибровки внутреннего генератора синхроим- пульсов микроконтроллера используется период гораздо меньшей длительности. Теоретически, в случае осциллятора микроконтроллера частотой 8 МГц, на восемь миллионов импульсов должно уйти столько же времени, сколько и на 32768 импульсов микросхемы “часов” (то есть, одна секунда), и для калибровки следует просто сопоставить эти два временных отрезка. Однако на практике совсем не обязательно вести подсчет восьми миллионов импульсов, — в качестве эталона вполне можно использовать более короткий промежуток времени и, соответственно, значительно сократить количество опера- ций подсчета. Если выбранное количество импульсов окажется недостаточным для достижения заданного периода времени, мы изменим значение в соответст- вующем регистре, чтобы “догнать” часы, а если слишком велико — изменим это значение, чтобы “подождать” часы. Эту подстройку будем выполнять в цикле до тех пор, пока не добьемся максимально возможной точности измерения. С целью калибровки (для микроконтроллера ATmegal69, содержащего для этого особые регистры) разработаем специальную функцию, код которой пред- ставлен в листинге 4.13.
Проект “Часы реального времени” 181 Листинг 4.13. Функция калибрования генератора синхроимпульсов ATmega169 void Calibration(void) { unsigned char calibrate = 0; int temp; unsigned char tempL; CLKPR = _BV(CLKPCE); /* Устанавливаем разряд разрешения изменения коэффициента масштабирования частоты генератора в регистре CLKPR микроконтроллера ATmega 169 */ CLKPR = _BV(CLKPS1) | _BV(CLKPS0); //Коэффициент деления = 8, //Внутр. RC 8МГц / 8 = 1МГц TIMSK2 = 0; //Сбрасываем разряды OCIE2A и TOIE2 ASSR = _BV(AS2); //Использование внешнего осциллятора для Т/С2 OCR2A = 200; //Инициализация регистра сравнения А Т/С2 TIMSK0 = 0; //Удаляем все источники прерываний TCCR1B = _BV(CS10); //Запускаем Т/Cl без предделения частоты TCCR2A = (KCCS20); //Запускаем Т/С2 без предделения частоты while((ASSR & 0x01) | (ASSR & 0x04)); //Ожидание сброса //разрядов TCN2UB и TCR2UB for(int i = 0; i < 10; i++) delay__loop_2 (30000) ; //Ожидание стабилизации внешнего //генератора while(’calibrate) { cli(); //Общий запрет прерываний TIFR1 = OxFF; //Удаление флагов TIFR1 и TIFR2 TIFR2 = OxFF; TCNT1H = 0; //Обнуляем счетный регистр Т/С1 TCNT1L = 0; TCNT2 = 0; //Обнуляем счетный регистр Т/С1 // Ожидаем установки флага при совпадении счетного регистра //Т/С2 с регистром сравнения А while ( ! (TIFR2 && (1«OCF2A) ) ); TCCR1B = 0; //Останов Т/Cl sei (); //Общее разрешение прерываний if ( (TIFR1 && (1«TOV1) ) ) //Если переполнение счетчика, . . . { temp = OxFFFF; } else //Если переполнения Т/Cl нет, то { //считываем значение счетного регистра tempL = TCNT1L; temp = TCNT1H; temp = (temp « 8) ; temp += tempL; } if (temp > 6250) //Если внутренний оциллятор "спешит",... {
182 Глава 4. Программные примеры для микроконтроллеров AVR Листинг 4.13. Окончание OSCCAL—; //уменьшаем значение регистра калибровки OSCCAL } else if (temp < 6120) //Если внутренний оциллятор "отстает",... { OSCCAL++; //увеличиваем значение регистра калибровки OSCCAL } else calibrate = 1; //Частота внутреннего осциллятора корректна TCCR1B = (l«CS10) ; // Запускаем Т/С1 } 2 ПРИМЕЧАНИЕ В функции calibration () используются регистры CLKPR (Clock Prescale Register) и OSCAL (Oscillator Calibration Register), характерные для микроконтроллеров ATmega169. Более подробно использование этих регистров рассмотрено в соответствующем техниче- ском описании. Отметим только, что в регистре CLKPR .разряд CLKPCE (разряд 7) разре- шает/запрещает изменение коэффициента деления (так, оператор CLKPR = _BV(CLKPCE); разрешает такое изменение). Разряды 0-3 регистра CLKPR (разряды CLKPSO, CLKPS1, CLKPS2 и CLKPS3) формируют значение коэффициента деления. Мы устанавливаем в лог. 1 только разряды CLKPS1 и CLKPSO, в результате чего получается значение ООН, которому соответствует коэффициент деления 8. При делении частоты ге- нератора 8 МГц на 8 получаем 1 МГц. Для того чтобы внешняя микросхема “часов” могла быть подключена к выво- ду 24 (TOSC1) микроконтроллера для тактирования таймера/счетчика Т/С2, уста- навливается разряд 3 (AS2) регистра ASSR. Калибровка выполняется в цикле, в котором мы настраиваем внутренний ос- циллятор и сравниваем получаемые результаты с внешними “часами”. Цикл вы- полняется до тех пор, пока результат не окажется в заданном “коридоре”. В начале функции объявляется и устанавливается в FALSE флаг calibrate. Когда этот флаг получит значение TRUE цикла завершится. При этом на каждом проходе выполняются следующие операции: 1. Общий запрет прерываний. 2. Очищаются флаги прерываний от таймеров. 3. Обнуляются счетные регистры. 4. Ожидание, пока Т/С2 не достигнет предельного значения: while ( ! (TIFR2 && (1«OCF2A)) ); 5. Останавливаем Т/С 1 (TCCR1В = 0;). 6. Общее разрешение прерываний. 7. Проверяем, не возникло ли переполнение Т/Cl? Если да, то устанавливаем пе- ременную temp равной OxFFFF. 8. Если состояние переполнения не возникло, считываем значение Т/Cl в пере- менную temp.
Проект “Часы реального времени” 183 9. Проверяем значение переменной temp. Если оно больше 6250, то выполняем декремент значения регистра калибровки осциллятора OSCCAL. В противном случае, если значение переменной temp меньше 6120, увеличиваем значение регистра OSCCAL. Если же значение переменной temp находится в диапазоне между 6250 и 6120, калибровка завершена, и наша миссия окончена. 10. Напоследок запускаем Т/С 1. Размещение строк во флэш-нам я mu Микроконтроллеры имеют довольно ограниченный объем памяти, особенно RAM. Поскольку память ROM намного дешевле оперативной, ее объем обычно гораздо больше, чем объем RAM. Микроконтроллеры AVR оборудованы памятью Flash ROM, функционирующей подобной постоянной памяти, хотя ее содержимое можно обновлять с помощью специального оборудования и специальных про- граммных функций. Определяя массив констант, их неплохо размещать в ROM, поскольку эле- менты такого массива по определению не должны изменяться. Однако компиля- тор с языка С всегда размещает массивы в области данных RAM-памяти, поэтому если в программе используется множество неизменяемых данных, представлен- ных в виде массива (например, строки, таблицы преобразований и т.п.), то при- дется попусту тратить на их размещение бесценные байты памяти RAM. К счастью, компилятор WinAVR позволяет обойти этот “риф” с помощью мо- дификатора PROGMEM. Пример сохранения строки и массива во флэш- памяти: const char SOME_STRING[] PROGMEM = "This is a string.\r\0"; ПРИМЕЧАНИЕ Модификатор progmem не является частью стандартного языка С, и специфичен для ком- пилятора WinAVR. Микроконтроллеры AVR имеют специальную команду программной загрузки в память 1pm, позволяющую получить данные из Flash ROM, однако такая коман- да слишком низкоуровневая и ее поддержка отсутствует в стандартом языке С. Именно поэтому и используется модификатор PROGMEM, который представляет собой программную оболочку для команды 1pm (объявлен в файле avr\ pgmspace. h). Создадим заголовочный файл Messages.h (листинг 4.14) и определим в нем строки-константы, которые в дальнейшем будут использованы в проекте. еФайл Messages . h можно также найти на прилагаемом к книге компакт-диске в папке Projects\AVR\RTC. Листинг 4.14. Файл Messages. h const const const char BAD_COMMAND1[] PROGMEM = "\rYou char BAD_COMMAND2[] PROGMEM = '" - I char ENTER[] PROGMEM = "Enter "; sent: don' t f H . f understand.\r"; const ’bdate char TEXT_GET[] PROGMEM = "'get' to • \r"; get the time and
184 Глава 4. Программные примеры для микроконтроллеров AVR Листинг 4.14. Окончание const char TEXTJSEC[] PROGMEM = " 'secXX' to set the second"; const char TEXT—MIN[] PROGMEM = " 'minXX' to set the minute"; const char TEXT HOUR[] PROGMEM = "'hourXX' to set the hour"; const char TEXT TOXX[] PROGMEM = " to XX.\r"; const ’bless char than ERROR_NUMBER[] PROGMEM f = "\rERROR - number must be const char ERROR 60[] PROGMEM = " 60.\r"; const char ERROR 12[] PROGMEM = " 12.\r"; const char THE—TIME—IS [] PROGMEM = = "The time is: "; Преобразование времени, выраженного в секундах, в понятную форму с помощью двоично-десятичной арифметики В проекте часов реального времени с помощью таймера микроконтроллера будет получено время в секундах. Необходимо разработать какой-нибудь метод преобразования значений счетчика, в удобочитаемый формат времени. Можно предложить несколько таких методов, однако мы рассмотрим только один из них, который позволит преобразовать данные, имеющие размер байта, в текстовую строку ASCII-символов. При этом будем использовать формат двоично- десятичных чисел BCD. Формат BCD (Binary Coded Decimal) — это специальный метод кодировки, который упрощает хранение и преобразование двоичных чисел (например, коли- чества подсчитанных импульсов) в десятичные числа, которые мы привыкли ви- деть на ЖК-дисплее часов. Суть этого метода состоит в том, чтобы разделить восьмиразрядный байт на две четырехразрядные тетрады (nibble), каждая из кото- рых может представлять числа в диапазоне от 0 до 16. Таким образом, в каждой тетраде мы можем сохранить одно десятичное число в диапазоне от 0 до 9, а в байте, состоящем из двух тетрад, — два таких числа. Если десятичное число, представленное байтом, меньше, чем 99, то мы можем преобразовать его в BCD-байт, используя следующий алгоритм. 1. Пусть у нас есть некоторый байт, состоящий из двух цифр: char num = 54; 2. Объявляем переменную для хранения значения старшей тетрады: char high = 0; 3. Вычисляем количество десятков в значении переменной num: while (num >= 10) { high++; num -= 10; } 4. Теперь переменная num содержит только одно целое число, соответствующее значению неполного десятка, а переменная high — количество полных десят- ков начального значения, то есть, в нашем случае high = 5, a num = 4. Нам ос-
Проект "Часы реального времени” 185 тается лишь объединить оба значения, разместив их в старшей и младшей тет- радах преобразованного в BCD-форму байта, соответственно: convertedbyte = (high << 4) | num; Оформим этот алгоритм в виде отдельной функции, которую затем будем ис- пользовать в проекте (листинг 4.15). Листинг 4.15. Функция преобразования байта в BCD-значение char CharToBCD(char input) char high = 0; while (input >= 10) { high++; input -= 10; } return (high << 4 ) i input; i Для преобразования значения, возвращенного функцией CharToBCD, в симво- лы ASCII можно воспользоваться тем фактом, что численным эквивалентом сим- вола ASCII ' 0' является значение 48, а каждого последующего символа, пред- ставляющего очередную десятичную цифру, — значение, на 1 большее предыду- щего. Проще говоря, это означает, что если к числу 4 добавить значение 48, соот- ветствующее символу ASCII 'О', то в результате будет получен ASCII-символ ' 4 ' (48 + 4 = 52, а значение 52 является десятичным представлением символа ' 4 '). Поэтому для преобразования десятичных целых цифр в эквивалентные им ASCII-символы следует просто прибавить к каждой такой цифре значение 48. Поскольку после результат, возвращенный функцией CharToBCD, содержит количества десятков и единиц, мы должны каким-то образом извлечь их, чтобы к ним по отдельности можно было прибавить 48 для окончательного преобразова- ния исходного числа в ASCII-представление. Например, если переменная Tens должна содержать десятки, а переменная Ones — единицы, то извлечь их содер- жимое можно следующим образом: Tens = CharToBCD(some_number); Ones = Tens; Ones = (Ones & OxOF) + 'O'; Для окончательного преобразования значения десятков в ASCII-вид необхо- димо сдвинуть вправо на 4 разряда значение переменной Tens: Tens = (Tens >> 4) + 'О'; Программная реализация часов реального времени Для реализации часов реального времени воспользуемся таймером/счетчиком Т/С2, тактируемым с помощью внешней микросхемы генерации импульсом с час- тотой 32768 Гц. При подсчете 32768-го импульса мы определяем, что прошла ровно одна секунда и следует сгенерировать прерывание, необходимое для вы- полнения обновления текущего времени.
186 Глава 4. Программные примеры для микроконтроллеров AVR Наш проект будет состоять из пяти файлов: CommWithPC. h (листинг 4.16) — заголовочный файл, используемый в CommWithPC. с; CommWithPC. с (листинг 4.18) — главный файл проекта, в котором реали- зована функция калибровки осциллятора микроконтроллера, инициализа- ции приемопередатчика UART и обмен данными с ПК; RTC. h (листинг 4.17) — заголовочный файл, используемый в RTC. с; RTC. с (листинг 4.19) — файл, содержащий функции анализа команд, при- ятных от ПК, а также, собственно, функции реализации часов; Messages . h — рассмотренный ранее файл сообщений (см. листинг 4.14). Листинг4.16. Файл CommWithPC.h #include <avr/io.h> #include <avr/interrupt.h> //Для доступа к функциям sei() и cli() #include <avr/delay.h> //Для доступа к функции _delay_loop_2 #include <avr/signal.h> //Для доступа к макросу INTERRUPT #include <stdlib.h> #include <avr/pgmspace.h> //Для доступа к модификатору PROGMEM ttinclude "RTC.h" void Calibration(void); //Калибровка осциллятора void UARTinit(void); //Инициализация UART char CharReceived(void) ; //Проверка, принят ли UART символ void SendChar(char) ; //Передача на ПК одного символа void SendString(char *); //Передача на ПК строки void SendFlashStr(const char *); //Передача на ПК строки из //флэш-памяти Листинг4.17. Файл RTC.h void Init(void); //Выполнение всех видов инициализации void Parseinput(char *); //Анализ строки, принятой от ПК void SetSecond(char *) ; //Установка секунды void SetMinute(char *) ; //Установка минуты void SetHour(char *); //Установка часа char CharToBCD(char input); //Преобразование байта в BCD-значение void InitTC2(void); //Инициализация Т/С2 void ShowClock(void); //Отображение часов void SetClock(void) ; //Установка часов Листинг4.18. Файл CommWithPC.с #include "RTC.h" #include "CommWithPC.h" int main(void) { char string[64]; //Буфер для хранения принятых от ПК команд
Проект “Часы реального времени” 187 Листинг 4.18. Продолжение unsigned char count = 0; Init(); /*Выполняем калибровку осциллятора, инициализацию UART и Т/С2; передаем на ПК строки с информаицей о возможных командах - функция реализована в файле RTC.c */ while(l); //Начинаем взаимодействие с ПК { if( CharReceived() == 1 ) //Если принят символ от ПК,... { string[count++] = UDRO; //извлекаем его и помещаем в конец //буфера //Длина принятой команды не должна превышать 64 символа if(string[count-1] == '\n') //Если строка оканчивается { //символом перехода на новую строку, то string[count-2] = ’\0’; //удаляем этот символ ParseInput(string); //Анализируем принятую команду string[O] = ’\0'; //Обнуляем буфер count — 0; } else //Если принята строка без спецсимволов в конце, if(count > 64) //проверяем ее длину, и если она >64, то { count = 0; //обнуляем буфер и передаем на ПК сообщение string[0] = т\0'; //об ошибке Sendstring("Error - received > 64 characters"); } ) } return 0; } //Функция, возвращающая 1, если через UART принят от ПК символ char CharReceived() { //О приеме сигнализирует установка разряда RX0 в регистре UCSR if ( (UCSR0A & (0x80)) ) return 1; else return 0; } //Функция отправки символа на ПК void SendChar(char data) { int i = 0; UDRO = data; //Для начала передачи помещаем символ в регистр //данных UART if(SREG & 0x80) //Если установлено общее разрешение прерываний, { //ожидаем, пока не будет передан байт или исчерпан счетчик while ( !(UCSR0A&0x40) && (i<10000) ) {
188 Глава 4. Программные примеры для микроконтроллеров AVR Листинг 4.18. Продолжение } else //Если нет общего разрешения прерываний, то просто //ожидаем окончания передачи байта while( !(UCSR0A&0x40) ); UCSR0A = UCSROA | 0x40; //Сбрасываем флаг ТХС } //Функция передачи на ПК строки s void SendString(char s[]) { int i = 0; while(i < 64) //Просматриваем строку { if( s[i] == *\0' ) break; //Если достигнут символ конца //строки, то выходим из цикла SendChar(s[i++]); //Передаем на ПК текущий символ } } //Функция передачи на ПК строки, размещенной во флэш-памяти void SendFlashStr(const char *pFlashStr) { unsigned char i; //Цикл for прерывается, если байт = '\0' или i = 60. Значение //60 выбрано с целью корректного ограничения длины строки for (i = 0; pgm_read_byte(&pFlashStr[i]) && i < 60; i++) { SendChar(pgm_read_byte(SpFlashStr[i])); } } //Функция инициализации приемопередатчика UART void UARTinit() { //Повышаем частоту до 2 МГц, чтобы получить скорость передачи //19200 бод CLKPR = _BV(CLKPCE); //Разрешаем изменение коэффициента // деления частоты осциллятора CLKPR = _BV(CLKPS1); //Коэффициент = 4, частота = 8 / 4 = 2МГц // Устанавливаем скорость передачи 19200 бод UBRR0H = 0; UBRR0L = 12; UCSROA = _BV(U2X0); //Разрешаем обмен с удвоенной скоростью /* Разрешаем приемник и передатчик. (Запись 1<<N означает сдвиг 1 на N разрядов влево, что равнозначно записи _BV(N). То же самое относится и к 0<<N) */ UCSR0B = (l«RXEN0) | (KCTXEN0) | (0«RXCIE0) | (0CCUDRIE0) ;
Проект “Часы реального времени" 189 Листинг 4.18. Продолжение //Асинхронная передача 8 бит без бита четности и с одним стоп-битом UCSR0C = (O«UMSELO) | (0«UPM00) | (0«USBS0) | (3«UCSZ00) | (0«UCPOL0) ; sei(); //Общее разрешение прерываний PCMSK1 = _BV(PINB6) | _BV(PINB4); //Маскируем прерывания по //изменению уровня сигнала на выводах порта В EIFR = _BV(PCIF1); //Сбрасываем флаг внешнего прерывания 1 EIMSK = _BV(PCIE1); //Разрешаем внешнее прерывание 1 } //Функция калибрования осциллятора микроконтроллера void Calibration(void) { unsigned char calibrate = 0; int temp; unsigned char tempL; CLKPR = _BV(CLKPCE); //Устанавливаем разряд разрешения //изменения коэффициента масштабирования частоты генератора //в регистре CLKPR микроконтроллера ATmega 169 CLKPR = _BV(CLKPS1) | _BV(CLKPS0); //Коэффициент деления = 8, //Внутр. RC 8МГц / 8 = 1МГц TIMSK2 = 0; //Сбрасываем разряды OCIE2A и TOIE2 ASSR = _BV(AS2); //Использование внешнего осциллятора для Т/С2 OCR2A = 200; //Инициализация регистра сравнения А Т/С2 TIMSK0 = 0; //Удаляем все источники прерываний TCCR1B = _BV(CS10); //Запускаем Т/Cl без предделения частоты TCCR2A = (l«CS20) ; //Запускаем Т/С2 без предделения частоты while((ASSR & 0x01) | (ASSR & 0x04)); //Ожидание сброса //разрядов TCN2UB и TCR2UB for(int i = 0; i < 10; i++) delay_loop_2(30000); //Ожидание стабилизации внешнего //генератора while(•calibrate) { cli(); //Общий запрет прерываний TIFR1 = OxFF; //Удаление флагов TIFR1 и TIFR2 TIFR2 = OxFF; TCNT1H = 0; //Обнуляем счетный регистр Т/С1 TCNT1L = 0; TCNT2 = 0; //Обнуляем счетный регистр Т/С1 // Ожидаем установки флага при совпадении счетного регистра //Т/С2 с регистром сравнения А while ( ’ (TIFR2 && (1«OCF2A) ) ); TCCR1B = 0; //Остановка Т/Cl sei(); //Общее разрешение прерываний if ( (TIFR1 && (1«TOV1) ) ) //Если есть переполнение счетчика, { temp = OxFFFF; } else //Если переполнения Т/Cl нет, то
190 Глава 4. Программные примеры для микроконтроллеров AVR Листинг 4.18. Окончание { //считываем значение счетного регистра tempL = TCNT1L; temp = TCNT1H; temp = (temp « 8) ; temp += tempL; } if (temp > 6250) //Если внутренний оциллятор "спешит",то... { OSCCAL--; //уменьшаем значение регистра калибровки OSCCAL } else if (temp < 6120) //Если внутренний оциллятор "отстает",... { OSCCAL++; //увеличиваем значение регистра калибровки OSCCAL } else calibrate =1; //Частота внутреннего осциллятора корректна TCCR1B = (KCCS10) ; // Запускаем Т/С1 } } Листинг4.19. Файл RTC.с ttinclude "CommWithPC.h" #include "Messages.h" //Переменные для хранения секунд, минут и часа Unsigned char gSECOND; unsigned char gMINUTE; unsigned char gHOUR; //Функция начальной инициализации void Init() { Calibration(); //Калибрование осциллятора микроконтроллера UARTinit(); //Инициализация UART InitTC2(); //Инициализация T/C2 SendFlashStr(ENTER); //Передаем на ПК строки приветствия SendFlashStr(TEXT_GET); SendFlashStr(ENTER); SendFlashStr(TEXT_SEC); SendFlashStr(TEXT_TOXX); SendFlashStr(ENTER); SendFlashStr(TEXT_MIN); SendFlashStr(TEXT_TOXX); SendFlashStr(ENTER); SendFlashStr(TEXT_HOUR); SendFlashStr(TEXT_TOXX); }
Проект “Часы реального времени” 191 Листинг 4.19. Продолжение /* Функция анализа принятой от ПК команды. Используем команды: get - для получения текущего времени secSS - установка секунд SS; minMM - установка минут ММ hourHH - установка часа НН */ void Parseinput(char s[]) { switch (s[0]) //Идентифицируем команду по ее первому символу { case 'д': if( <s[l] == 'е') && (s[2] == 't') ) //Команда get ShowClock(); //Передаем на ПК текущее время break; case 's': if( <s[l] == 'e') && (s[2] == 'с') ) //Команда sec SetSecond(s); //Устанавливаем секунды break; case 'm': if( <s[l] == 'i') && (s[2J == 'n') ) //Команда min SetMinute(s); //Устанавливаем минуты break; case 'h': if( (s[l] == 'o') && (s[2] == 'u') && <s[3] == 'r')) SetHour(s); //Команда hour - устанавливаем час break; default: SendFlashStr(BAD_COMMAND1); //Если корректная команда SendChar(s[0]); //не получена, передаем SendFlashStr(BAD_COMMAND2); //ПК сообщения об ошибке break; } s[0] = '\0'; //Обнуляем команду } //Функция установки секунд на основании принятой команды sec void SetSecond(char s[]) { char str[] = {0,0,'\0'}; //Буфер для хранения символов секунд int sec; str[0] = s[3]; //Извлекаем символы секунд из команды str[1] = s[4]; sec = atoi(str); //Преобразуем строку в число if(sec <= 60) //Если значение секунд корректно, то { gSECOND = (unsigned char)sec; //сохраняем его в gSECOND } else //В противном случае передаем на ПК сообщение об ошибке { SendFlashStr(ERROR_NUMBER); SendFlashStr (ERROR__60) ; } } //Функция установки минут на основании принятой команды min //См. примечания к функции SetSecond
192 Глава 4. Программные примеры для микроконтроллеров AVR Листинг 4.19. Продолжение void SetMinute(char s[]) { char str[] = {0,0, '\0'}; int min; str[0] = s[3] ; str[1] = s[4] ; min = atoi(str); if( min <= 60) { gMINUTE = (unsigned char)min; } else { SendFlashStr(ERROR_NUMBER); SendFlashStr(ERROR_60) ; } } //Функция установки часа на основании принятой команды hour //См. примечания к функции SetSecond void SetHour(char s[]) { char str[] = {0,0,'\0'}; int hour; str[0] = s [4] ; s tr[1] = s [ 5 ] ; hour = atoi(str); if( hour <= 12) { gHOUR = (unsigned char)hour; } else { SendFlashStr(ERROR_NUMBER); SendFlashStr(ERROR_12); } } //Функция передачи на ПК строки с информацией о текущем времени void ShowClock(void) { unsigned char HH, HL, MH, ML, SH, SL; HH = CharToBCD(gHOUR); //Получаем значение часа HL = (HH & 0x0F) + 'O’; HH = (HH » 4) + 'O’; MH = CharToBCD(gMINUTE); //Получаем значение минут ML = (MH & OxOF) + ’O'; MH = (MH » 4) + 'O'; SH = CharToBCD(gSECOND); //Получаем значение секунд
Проект “Часы реального времени” 193 Листинг 4.19. Продолжение SL = (SH & OxOF) + 'О’; SH = (SH » 4) + '0'; SendFlashStr(THE_TIME_IS); //Передаем на ПК строку со значением SendChar(НН); //времени SendChar(HL); SendChar(’:'); SendChar(MH); SendChar(ML); SendChar(':'); SendChar(SH); SendChar(SL); SendChar('\r'); //Функция преобразования байта в BCD-значение char CharToBCD(char input) { char high = 0; while (input >= 10) { high++; input -= 10; } return (high « 4) | input; //Функция инициализации T/C2 void InitTC2(void) { //Ожидаем стабилизации внешнего осциллятора for(int i = 0; i < 10; i++) _delay_loop_2(30000); cli(); //Общий запрет прерываний cbi(TIMSK2, TOIE2); //Запрет прерываний от Т/С2 ASSR = _BV(AS2); //Т/С2 будет тактирован от внешнего осциллятора TCNT2 = 0; //Обнуляем счетный регистр TCCR2A |= _BV(CS22) | _BV(CS20); //Выбор коэффициента деления: //32768 Гц / 128 = 1 с между моментами переполнения //Ожидаем сброса разрядов TCN2UB и TCR2UB while((ASSR & 0x01) | (ASSR & 0x04)); TIFR2 = OxFF; //Сбрасываем флаги прерываний sbi(TIMSK2, TOIE2); //Разрешаем прерывание при переполнении Т/С2 sei(); //Общее разрешение прерываний gSECOND = 0; //Обнуляем показания времени gMINUTE = 0; gHOUR = 0; 13-6-767
194 Глава 4. Программные примеры для микроконтроллеров AVR Листинг 4.19. Окончание //Подпрограмма обработки прерывания при переполнении Т/С2 //(один раз в секунду) INTERRUPT(SIG_OVERFLOW2) { gSECOND++; //Увеличиваем значение секунд if (gSECOND = 60) { //Если новая минута, .. gSECOND = 0; //секунда = 0, gMINUTE++; //увеличиваем значение минут if (gMINUTE > 59) { //Если новый час,... gMINUTE = 0; //минута = 0, gHOUR++; //увеличиваем значение часа if (gHOUR > 24) { //Если новые сутки,... gHOUR = 0; //час = 0 } } Напоследок осталось внести небольшие дополнения в файл makefile: # MCU name MCU = atmegal69 # Output format, (can be srec, ihex, binary) FORMAT = ihex # Target file name (without extension). TARGET = CommWithPC # List C source files here. (C dependencies are automatically ’^generated. ) SRC = $(TARGET).c SRC += RTC.c Все файлы этого проекта находятся на прилагаемом к книге компакт-диске в папке Projects\AVR\RTC.
глава 5 Программные примеры для микроконтроллеров PIC При использовании примеров, представленных в этой главе, может потребо- ваться изменить значения временных задержек перед записью программы в ко- нечное устройство. Отображение состояния выводов порта В листинге 5.1 представлена программа, которая опрашивает состояние выво- дов порта В (все выводы — входы) и передает соответствующую информацию че- рез приемопередатчик USART по интерфейсу RS232. Файлы для этого примера BitTest. с и BitTest. pj t находятся на прилагаемом к книге МС компакт-диске в папке Projects\PIC\BitTest. Листинг 5.1. Файл BitTest.с include <16F877.h> #include <stdio.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232 (baud=9600 ,parity=N,xmit=PIN_C6, rcv=PIN_C7, stream=RS232,bits—8) void main() { unsigned char c, bit_mask; //Переменные для счетчика выводов //и битовой маски проверки состояний port_b_pullups(TRUE); //Активизируем внутренние подтягивающие //резисторы для порта В set_tris_b(OxFF); //Все выводы порта В - входы bit_mask = 1; //Начинаем сопоставление с вывода О for (с=0; с<8; с++) //Просматриваем 8 разрядов порта В { if (input_b() & bit_mask) //Сопоставляем значение регистра //данных порта В с маской //Если в разряде, заданном маской, установлена лог. 1,... printf("Bit %d is high.\n", (int)c); else //Если в разряде, заданном маской, установлен лог. О,... printf("Bit %d is low.\n", (int)c); bit_mask «= 1; //Сдвигаем 1 в маске влево для проверки //состояния следующего разряда } while(l); //Бесконечная задержка 1 13*
196 Глава 5. Программные примеры для микроконтроллеров PIC Управление частотой мерцания светодиодов с помощью различных таймеров К выводам 0—2 порта D подключены светодиоды. Частотой мерцания свето- диода на выводе 0 управляет TMR0, светодиода на выводе 1 — TMR1, а светодио- да на выводе 2 — управляет TMR2. Реализация программы — в листинге 5.2. В Файлы для этого примера BlnkCtrl. с и BlnkCtrl. р j t находятся на прилагаемом к кни- ге компакт-диске в папке Projects\PIC\BlnkCtrl. Листинг 5.2. Файл BlnkCtrl. с #include <16F877.h> #fuses HS, WDT #use delay(clock=10000000) int cO, cl, c2; //Переменные, используемые для замедления мерцания #int_timerO //Обработчик прерывания при переполнении TMR0 void timerO_isr(void) { if (c0++ & 8) //Увеличиваем cO, и если 3-й разряд = 1,... output_bit(PIN_D0, 1); //включаем светодиод на выводе 0 else output_bit(PIN_D0, 0); //иначе выключаем светодиод на выводе 0 } #int_timerl //Обработчик прерывания при переполнении TMR1 void timerl_isr(void) { if (cl++ & 8) //Увеличиваем cl, и если 3-й разряд = 1,... output_bit(PIN_D1, 1); //включаем светодиод на выводе 1 else output_bit(PIN_D1, 0); //иначе выключаем светодиод на выводе 1 } #int__timer2 //Обработчик прерывания при переполнении TMR2 void timer2__isr (void) { if (c2++ & 8) //Ув еличиваем c2, и если 3-й разряд = 1,... output_bit (PIN_D2, 1); //включаем светодиод на. выводе 2 else output_bit(PIN_D2, 0); //иначе выключаем светодиод на выводе 2 } void main() { /* Инициализируем таймер TMR0 (функция setup_counters также используется для инициализации сторожевого таймера). Первый параметр указывает на тактирование внутренним сигналом, второй задает коэффициент деления частоты.
Управление светофорами на перекрестке 197 Листинг 5.2. Окончание Можно также было бы воспользоваться функцией setup__t imerO (RTCC_INTERNAL | RTCC_DIV_2) */ setup_counters(RTCC_INTERNAL, RTCC_DIV_2); //Инициализируем таймер TMR1. Коэффициент деления = 4 setup_t imer_l (T 1_INTERNAL | T 1_DIV_BY_4) ; ^Инициализируем TMR2. Коэффициент деления = 16; сброс значения счетчика - по 255 импульсу; один сброс перед прерыванием */ s е tup_timer_2(T2_DIV_BY_16, 255, 1) ; //Разрешаем прерывания при переполнении счетчиков enable_i interrupts (int_timerO) ; enable_interrupts(int_timerl); enable_interrupts (int__timer2) ; enable_interrupts(global); //Общее разрешение прерываний while(1); //Бесконечный цикл в ожидании прерываний } Управление светофорами на перекрестке Рассмотрим пример использования микроконтроллера PIC16F877 для управ- ления светофорами на перекрестке. Схема подключения светодиодов и кнопок за- проса на проход со стороны пешеходов, а также полного останова движения на перекрестке представлены в табл. 5.1 (светодиоды и кнопки соединены с микро- контроллером через резисторы 1 кОм). Таблица 5.1. Схема подключений для реализации управления перекрестком Направление Подключение Тип Вывод 1 Светодиод “Красный” 4 порта D 1 Светодиод “Желтый” 5 порта D 1 Светодиод “Зеленый" 6 порта D 1 Кнопка Запрос пешехода 0 порта В 2 Светодиод “Красный” 1 порта D 2 Светодиод “Желтый” 2 порта D 2 Светодиод “Зеленый” 3 порта D 2 Кнопка Запрос пешехода 1 порта В — Кнопка Останов движения 2 порта В В данном контексте “’направление” означает направление движения по одной из пересекаю- щихся дорог (рис. 5.1), а “запрос пешехода” — нажатие кнопки на пешеходном переходе в на- правлении, пересекающем текущее. Кнопка “Ос- танов движения” может использоваться дорож- ными службами. Реализация программы пред- ставлена в листинге 5.3. Файлы для этого примера CrossCtr. с и CrossCtr. рj t находятся на прилагаемом к книге компакт-диске в папке Projects\PIC\CrossCtr. Направление 1 Рис. 5.1. Направления на перекрестке
198 Глава 5. Программные примеры для микроконтроллеров PIC Листинг 5.3. Файл CrossCtr. с #include <16F877.h> #use delay(clock=l0000000) #fuses HS, WDT # byte PORTD = 8 //Определяем порты D и В # byte PINB = 6 //Определяем выводы для подключения светодиодов # bit dirl_red = PORTD.4 # bit dirl_yellow = PORTD.5 # bit dirl_green = PORTD.6 # bit dir2__red = PORTD. 1 # bit dir2_yellow = PORTD.2 # bit dir2_green = PORTD.3 //Определяем выводы для подключения кнопок # bit dirl_button = PINB.0 # bit dir2__button = PINB.l #bit stop__button = PINB. 2 char time_left; //Счетчик времени для текущего состояния int current_state; /* Флаг текущего состояния светофоров. Его значение соответствует объявленному ниже перечислимому типу */ char toggle_flag; /* Флаг, определяющий мигание красного сигнала светофора в состоянии "stop" (то есть, при запрете движения через перекресток) */ /* Перечислимый тип, определяющий числовые значения для идентификаторов состояния перекрестка. dirl_moving - в направлении 1 движение разрешено (зеленый свет), в направлении 2 - запрещено (красный свет); dirl_warning - направление 1 сейчас будет перекрыто, на обоих светофорах горит желтый свет; dir2_moving - в направлении 2 движение разрешено (зеленый свет), в направлении 1 - запрещено (красный свет); dir2_warning - направление 2 сейчас будет перекрыто, на обоих светофорах горит желтый свет; stop - в обоих направлениях движение запрещено, мигает красный свет */ enum {dirl_moving, dirl_warning, dir2_moving, dir2_warning, stop); void main() { port_b_pullups(TRUE); //Активизируем внутренние подтягивающие //резисторы для порта В set_tris_b(OxFF); //Все выводы порта В - входы set_tris_d(0x00); //Все выводы порта D - выходы current_state = dir2_warning; //Стартовое состояние - желтый //свет в обоих направлениях при разрешенном втором
Управление светофорами на перекрестке 199 Листинг 5.3. Продолжение while(l) //Б есконечный цикл, в котором состояние опрашивается { //через каждую секунду delay_ms(1000); //Задержка на 1 с switch(current_state) //Опрос текущего состояния { //Если разрешено движение в направлении 1 case dirl_moving: dirl_red = 1; dirl_yellow = 1; dirl_green = 0; //Зеленый свет для 1 dir2_red = 0; //Красный свет для 2 dir2_yellow = 1; dir2_green = 1; //Если нажата кнопка пешеходом или "стоп" if (dirl_button || stop_button) { //сокращает остаток времени до 10 с if (time_left > 10) time__left — 10; } if (time_left != 0) //Если время еще не { //истекло, уменьшаем --time_left; //счетчик на 1 с continue; //Переходим в начало цикла } time_left =5; //Если время истекло, даем // еще 5 с для желтого света //Включаем желтый предупреждающий сигнал current_state = dirl_warning; break; //Если разрешено движение в направлении 1, но уже включен //желтый предупреждающий сигнал case dirl_warning: dirl_red = 1; dirl_yellow = 0; //Желтый свет для 1 dirl_green = 1; dir2_red = 0; //Для 2 красный + dir2_yellow = 0; //желтый свет dir2_green = 1; if (time_left •= 0) //Е ели время еще не { //истекло, уменьшаем --time_left; //счетчик на 1 с continue; //Переходим в начало цикла } //Если время желтого света истекло,... if (stop_button) //Если нажато "стоп", //переходим в режим закрытия перекрестка current_state = blink; else { //Иначе, разрешаем на 30 с движение в //направлении 2 time left = 30;
200 Глава 5. Программные примеры для микроконтроллеров PIC Листинг 5.3. Продолжение current_state = dir2_moving; } break; //Если разрешено движение в направлении 2 case dir2_moving: dirl_red =0; dirl_yellow = 1; dirl_green = 1; dir2_red = 1; dir2_yellow = 1; dir2_green = 0; if (dir2_button || stop_button) { if (time_left > 10) time_left = 10; } if (time_left • = 0) { —time left; continue; } time_left = 5; current_state — dir2_warning; break; //Если разрешено движение в направлении 2, но уже включен //желтый предупреждающий сигнал case dir2_warning: dirl_red = 0; dirl_yellow = 0; dirl_green = 1; dir2_red = 1; dir2_yellow = 0; dir2_green = 1; if (time_left •= 0) { --time_left; continue; } if (stopjbutton) current_state = blink; else { time_left = 30; current_State = dirl_moving; } break; //Если активен запрет на движение через перекресток case blink: dirl_yellow =1; //На обоих светофорах dirl_green =1; //отключены зеленый и желтый dir2_yellow = 1; //сигналы dir2 green = 1;
Секундомер 201 Листинг 5.3. Окончание toggle_flag л= 1; //Переключаем состояние //красного сигнала на противоположное if (toggle_flag & 1) //Если флаг = 1, . . . { dirl_red = 1; //красный сигнал горит dir2_red = 0; } else { dirl red = 0; //иначе, красный сигнал гаснет dir2_red = 1; } //Если кнопка "стоп" отпущена, переходим в //рабочий режим if (! stop__button) current_state = dirl_warning; break; //Состояние по умолчанию - dir2_warning default: current_state = dir2_warning; break; } } 2 Секундомер В рассмотренной ниже программе время отслеживается с помощью прерыва- ния от таймера TMR1 через каждую миллисекунду. Запуск секундомера реализу- ем по нарастающему фронту сигнала на выводе INTO, а останов — по ниспадаю- щему фронту сигнала на том же выводе. Текущее значение секунды выводится через порт D и отображается с помощью светодиодов. С помощью функции setup_timer_l устанавливается коэффициент деления частоты системной синхронизации для тактирования таймера. Переполнение счет- чика должно возникать через каждые 0,001 с (то есть, 1 мс). Если коэффициент деления выбрать равным 1, то в случае рабочей частоты микроконтроллера в 10 МГц периоду 1 мс соответствует 0,001 • 10000000 = 10000 отсчетам. Поскольку с помощью 16 разрядов счетчика можно получить максимальное значение 65535 (OxFFFF), то он должен инициализироваться значением 65536 - 10000 = 55536. Реализация программы — в листинге 5.4. fc/Rb Файлы для этого примера Secundom. с и Secundom.pjt находятся на прилагаемом к кни- Ч& ге компакт-диске в папке Projects\PIC\Secundom. Листинг 5.4. файл Secundom. с ttinclude <16F877.h> #use delay(clock=10000000) #fuses HS, NOWDT
202 Глава 5. Программные примеры для микроконтроллеров PIC Листинг 5.4. Продолжение inti6 msecs; //Переменная для хранения значения миллисекунд int secs; //Переменная для хранения значения секунд unsigned char is_output; //Флаг, определяющий, активен ли в //данный момент вывод значения секунд в порт D //Подпрограмма обработки прерывания при переполнении TMR1 #int_timerl void timerl_isr(void) { if (is_output) //Если активен вывод секунд,... { if (++msecs == 1000) //Увеличиваем значение миллисекунды. { //Если оно равно 1000, то увеличиваем значение секунды //Если секунда после увеличения =60, то сбрасываем ее в 0 if (++secs == 60) secs = 0; msecs = 0; output_D(~secs); //Выводим в порт D значение секунды } } } //Подпрограмма обработки внешнего прерывания по входу INTO #int_ext void ext_isr(void) { delay_ms(100); //Ожидаем завершения дребезга контакта ключа if (is_output & 1) /* Если активен вывод секунд,... то это говорит о том, что возникло прерывание по ниспадающему фронту (ключ разомкнут), и теперь следует установить внешнее прерывание по нарастающему фронту */ ext_int_edge(0, Ъ_ТО_Н); else /* Если вывод секунд не активен, . . . { то это говорит о том, что возникло прерывание по нарастающему фронту (ключ замкнут), и следует запустить секундомер, а затем установить внешнее прерывание по ниспадающему фронту */ set__timerl (55536) ; //Инициализируем таймер и переменные msecs = 0; secs = 0; ext_int_edge(0, H_TO_L); //Инициализируем внешнее прерывание } is_output л= is_output; //Меняем состояние флага вывода } void main () { port_b_pullups(TRUE); //Активизируем внутренние подтягивающие //резисторы для порта В
Обмен данными в режиме PSP 203 Листинг 5.4. Окончание ext_int_edge(0, L_TO_H); //Внешнее прерывание - по нарастающему //фронту сигнала на выводе INTO is_output = 0; //Вывод данных отключен setup_timer_l(T1_INTERNAL | T1_DIV_BY_1); //Инициализируем TMR1 enable_interrupts(INT_EXT); //Разрешаем внешнее прерывание enable_interrupts(INT_TIMER1); //Разрешаем прерывание от TMR1 enable_interrupts(global); //Общее разрешение прерываний while(l); //Бесконечный цикл в ожидании внешнего прерывания } Обмен данными в режиме PSP Реализуем обмен данными между двумя микроконтроллерами PIC, один из которых (главный) работает в режиме PSP порта D, а второй (внешний) принимает и передает данные через порт С (см. раздел “Параллельные порты ввода/вывода” главы 1). Три вывода порта Е обоих микроконтроллеров служат для обмена управляющими сигналами: RD — вывод О, WR — вывод 1, CS — вывод 2. Главный микроконтроллер передает внешнему данные, принятые через порт В, а данные, принятые от внешнего, отображает с помощью светодиодов, подклю- ченных к порту С. При этом обмен данными осуществляется по соответствующе- му запросу на прерывание. Программа для главного микроконтроллера представ- лена в листинге 5.5. Внешний микроконтроллер постоянно считывает данные из главного микро- контроллера, а затем “эхом” отправляет их обратно. Программа для внешнего микроконтроллера представлена в листинге 5.6. е Файлы для этого примера PSP_1. с, PSP_2 . с, PSP_1. р j t и PSP_2 . р j t находятся на прилагаемом к книге компакт-диске в папке Projects\PIC\PSP. Листинг 5.5. Файл PSP1. а #include <16F877.h> #use delay(clock=10000000) #fuses HS, NOWDT #byte TRISE = 0x89 //Определяем регистр TRISE #int_PSP //Обработчик прерывания от PSP PSP_isr() { if (TRISE & 0x80) //Если запись в порт PSP (флаг IBF=1), output_C(~input_D()); //то выводим данные, принятые через //порт D, в порт С (на светодиоды) else //Если вывод через порт PSP, output_D(input_B()); //то выводим через порт D данные порта В } void main ()
204 Глава 5. Программные примеры для микроконтроллеров PIC Листинг 5.5. Окончание { port b pullups(TRUE); //Активизируем внутренние подтягивающие //резисторы для порта В setup_adc_ports(NO_ANALOGS); //Выводы порта Е не должны //использоваться в качестве аналоговых setup_psp(PSP_ENABLED); //Активизируем режим PSP enable_interrupts(INT_PSP); //Разрешаем прерывание от PSP enable_interrupts(global); //Общее разрешение прерываний while(l); //Бесконечный цикл в ожидании прерывания от PSP 2 Листинг 5.6. Файл psp_2 . о ttinclude <16F877.h> #use delay(clock=10000000) #fuses HS, WDT //Будет использоваться сторожевой таймер int с ; void main () { setup_adc_ports(NO_ANALOGS); //Выводы порта E не должны //использоваться в качестве аналоговых setup_counters(RTCC_INTERNAL, WDT_72MS); //Сброс от // сторожевого таймера через каждые 72 мс output_E(0x7); //CS=1, RD=1, WR=1 while(1) { restart_wdt(); //Сбрасываем счетчик сторожевого таймера output_E(2); //CS=O, RD=0, WR=1 - запрос главному //микроконтроллеру на чтение данных с = input_c(); //Считываем данные от главного //микроконтроллера через порт С в переменную с output_E(7); //CS=1, RD=1, WR=1 delay_us(50); //Задержка 50 мкс между операциями PSP ourput_C(с); //Передаем данные главному микроконтроллеру output_E(l); //CS=0, RD=1, WR=0 - запрос главному //микроконтроллеру на запись данных output_E(7); //CS=1, RD=1, WR=1 delay_us(50); //Задержка 50 мкс между операциями PSP } } Контроль предельной скорости вращения двигателя Таймер TMR1 работает в режиме захвата на входе. На вход ССР1 (например, вывод 2 порта С) подается сигнал от тахометра, измеряющего скорость вращения двигателя. Если период следования импульсов становится меньше 1 мс, то это го-
Контроль предельной скорости вращения двигателя 205 ворит о слишком высокой скорости вращения двигателя, а включается светодиод, подключенный к выводу 0 порта D. При измерении периода следования импульсов от тахометра следует учиты- вать возможность возникновения переполнения счетчика, поэтому в программе реализуем подсчет переполнений, а фактическое количество тактовых импульсов таймера будем вычислять по формуле: N Noverflows 0x10000 — "Njtart “I- Nend, где Noverflows — количество переполнений; Nstart — количество тактовых импульсов, принятых на начало периода; Nend — количество тактовых импульсов, принятых на конец периода. При рабочей частоте микроконтроллера 10 МГц и коэффициенте деления 1 за одну секунду счетчик подсчитывает 10000 импульсов, поэтому будем сопостав- лять полученную величину периода именно с этим значением. Реализация этого алгоритма представлена в листинге 5.7. Файлы для этого примера SpeedCtr. с и SpeedCtr . р j t находятся на прилагаемом к кни- ге компакт-диске в папке Projects\PIC\SpeedCtr. Листинг 5.7. файл SpeedCtr. с ttinclude <16F877.h> #use delay(clock=10000000) #fuses HS, NOWDT int N_overflows; unsigned long int N_start, N__end; long int N; #int_timerl //Обработка прерывания при переполнении TMR1 timer1 isr() { ++N_overflows; //Увеличиваем счетчик переполнений } #int_CCPl //Обработ ка прерывания при захвате на входе CCPl_isr() { N_end = ССР_1; //Количество тактовых импульсов, подсчитанных //с момента поступления предыдущего импульса на входе ССР1 //Вычисляем период между двумя последовательными импульсами ССР1 N = (N_overflows * 0x10000) - N_start + N end; if (N < 10000) //Если период меньше 10000 импульсов, то... output_low(PIN_D0); //загорается сигнальная лампочка else output_high(PIN_D0); //Иначе лампочка гаснет N_start = N_end; //Запоминаем позицию текущего замера N__overfl°ws = //Сбрасываем счетчик переполнений } void main ()
206 Глава 5. Программные примеры для микроконтроллеров PIC Листинг 5.7. Окончание { setup_ccpl(CCP_CAPTURE_RE); //Захват по нарастающему фронту //Инициализируем TMR1: тактируется от внутреннего осциллятора, //без предварительного деления частоты setup_timer_l(T1_INTERNAL | T1_DIV_BY_1); //Разрешаем прерывание при переполнении TMR1 enable_interrupts(INT_TIMER1); //Разрешаем прерывание при захвате на входе enable_interrupts(INT_CCP1); enable_interrupts(global); //Общее разрешение прерываний while(l); //Бесконечный цикл в ожидании сигнала на входе ССР1 } Обмен данными по шине CAN Рассмотренная ниже программа каждую секунду выдает в шину CAN восемь последовательных чисел в диапазоне от 0 до 255. В то же время выполняется “прослушивание” шины CAN на предмет принятых данных с их последующей пе- редачей на ПК через USART. В программе используется внешний файл canbus . с с библиотечными функциями CAN от компании CCS. Содержимое этого файла представлено в листинге 5.8, а содержимое его заголовочного файла — в листин- ге 5.9. Исходный текст программы обмена данными по шине CAN представлен в листинге 5.10. е Файлы для этого примера CAN. с, CAN. р j t, а также canbus . с и canbus . h находятся на прилагаемом к книге компакт-диске в папке Proj ects\PIC\CAN. Листинг 5.8. Файл canbus. с /* Функции CAN для микроконтроллеров Р1С18Схх8 и 18Fxx8: can_init - конфигурирует шину CAN can_set_baud - устанавливает скорость передачи can_set_mode - переводит модуль CAN в тот или иной режим can_set_id - инициализирует стандартный и расширенный ID can_get_id - возвращает стандартный и расширенный ID can_putd - передает сообщение/запрос с указанным ID can_getd - возвращает заданное сообщение/запрос и ID can_kbhit - возвращает TRUE, если в буфере приема есть данные can_tbe - возвращает TRUE, если буфер передачи готов к передаче can_abort - прерывает все незавершенные сеансы передачи Вывод чтения CANRX - PIN_B3, вывод передачи CANTX - PIN В2. */ #include <canbus.h> /* Функция can_init() инициализирует шину CAN микроконтроллеров Р1С18ххх8. Устанавливает фильтр и маски приема таким образом, чтобы шина принимала все входящие ID. Конфигурирует оба буфера чтения на прием только корректных сообщений. Вывод В2 делает выходом^ а вывод ВЗ - входом */
Обмен данными по шине CAN 207 Листинг 5.8. Продолжение void can_init(void) { can_set_mode(CAN_OP_CONFIG); can_set_baud () ; RXBOCON = 0; RXBOCON.rxm = CAN_RX_VALID; RXBOCON.rxbOdben = CAN_USE_RX_DOUBLE_BUFFER; RXB1CON=RXB0 CON ; CIOCON.endrhi = CAN_ENABLE_DRIVE_HIGH; CIOCON.cancap = CAN_ENABLE_CAN_CAPTURE; can_set_id(&RXM0EIDL, CAN_MASK_ACCEPT_ALL, 0); //Маска 0 can_set_id(&RXFOEIDL, 0, 0); //Фильтр 0 маски 0 can_set_id(&RXFlEIDL, 0, 0); //Фильтр 1 маски 0 can_set_id(&RXM1EIDL, CAN_MASK_ACCEPT_ALL, 1); //Маска 1 can_set_id(&RXF2EIDL, 0, 1); //Фильтр 0 маски 1 can_set_id(&RXF3EIDL, 0, 1); //Фильтр 1 маски 1 can_set_id(&RXF4EIDL, 0, 1); //Фильтр 2 маски 1 can_set_id(&RXF5EIDL, 0, 1); //Фильтр 3 маски 1 set_tris_b((*0xF93 & OxFB ) | 0x08); //ВЗ - выход, В2 - вход can_set_mode(CAN_OP_NORMAL); } /* Функция can_set_baud() конфигурирует регистры управления, определяющие скорость передачи. */ void can_set_baud(void) { BRGCONl.brp = CAN_BRG_PRESCALAR; BRGCONl.sjw = CAN_BRG_SYNCH_JUMP_WIDTH; BRGCON2.prseg = CAN_BRG_PROPAGATION_TIME; BRGCON2.seglph = CAN_BRG_PHASE_SEGMENT_1; BRGCON2 . sam = CAN_BRG_SAM; BRGCON2.seg2phts = CAN_BRG_SEG_2_PHASE_TS; BRGCON3.seg2ph = CAN_BRG_PHASE_SEGMENT_2; BRGCON3.wakfil = CAN_BRG_WAKE_FILTER; } void can_set_mode(CAN_OP_MODE mode) { CANCON.reqop=mode; while( (CANSTAT.opmode) != mode ); } /* Функция can_set_id() конфигурирует регистры xxxxEIDL, xxxxEIDH, xxxxSIDL и xxxxSIDH для настройки определенных буферов на использование заданного ID. Параметры: addr - указатель на первый байт ID регистра, начиная с xxxxEIDL; id - ID для инициализации буфера; ext - TRUE, если буфер использует расширенный ID */
208 Глава 5. Программные примеры для микроконтроллеров PIC Листинг 5.8. Продолжение void can_set_id(int* addr, long int id, inti ext) //inti = bit { int *ptr; ptr = addr; if (ext) //Расширенный ID { //eidl *ptr = //eidh ptr--; makeS(id, 0); //0:7 *ptr = //sidl ptr--; make8(id, 1); //8:15 *ptr = make8((int32)id, 2) & 0x03; //16:17 *ptr |= (makeS((int32)id, 2) « 3) & OxEO; //18:20 *ptr |= 0x08; //si dh ptr--; *ptr = ((make8((int32)id, 2) » 5) & 0x07 ); //21:23 *ptr |= ((make8((int32)id, 3) « 3) & 0xF8); //24:28 } else { //eidl *ptr = 0; //eidh ptr--; * ptr = 0; / /sidl ptr--; * ptr = (make8(id, 0) « 5) & OxEO; //sidh ptr--; * ptr = (make8(id, 0) » 3) & OxlF; * ptr |= (make8(id, 1) « 5) & OxEO; } } /* Функция can_get_id() возвращает ID заданного буфера. Используется после приема сообщения для определения ID, от которого было получено сообщение. Параметры: addr - указатель на первый байт ID регистра, начиная с xxxxEIDL (например, указатель на RXM1EIDL); ext - TRUE, если буфер использует расширенный ID. */ long int can_get_id(int * addr, inti ext) //inti = bit { long int ret; int * ptr;
Обмен данными по шине CAN 209 Листинг 5.8. Продолжение ret = 0; ptr = addr; if (ext) { ret = *ptr; ptr--; ret |= ((long ptr--; ret |= ((long int) *ptr int) *ptr « 8) ; & 0x03) - C< 16; //eidl //eidh //sidl ret |= ((long int) *ptr & OxEO) - C< 18; ptr--; ret |= ((long int)*ptr } else { ptr -= 2 ; ret = ( (long int)*ptr & « 21) ; OxEO) >: > 5; //sidh //sidl ptr— ; ret |= ((long } return(ret); } int) *ptr « 3) ; //sidh /* Функция can_putd() помещает данные в буфер передачи. Параметры: id - ID, которому передаются данные; data - указатель на массив передаваемых данных; 1еп - длина массива передаваемых данных; priority - приоритетность сообщения от 0 до 3 (чем выше номер, тем быстрее сообщение будет отправлено); ext - TRUE, если используется расширенный ID; rtr - TRUE для установки разряда RTR (запрос) в ID. В случае успешного размещения данных возвращается номер буфера (от 0 до 2). В противном случае возвращается значение OxFF */ int can_putd(long int id, int * data, int len, int priority, inti ext, inti rtr) //inti = bit { int i ; int * txdO; int ret; txdO = &TXRXBaD0; //Поиск пустого передатчика по адресу банка доступа if (’TXBOCON.txreq) { CANCON.win = CAN_WIN_TX0; ret=0; } 14-6-767
210 Глава 5. Программные примеры для микроконтроллеров Р1С Листинг 5.8. Продолжение else if (’ТХВICON. txreq) { CANCON.win = CAN_WIN_TX1; ret = 1; } else if (’TXB2CON.txreq) { CANCON.win = CAN_WIN_TX2; ret=2; } else return(OxFF); TXBaCON.txpri=priority; //Устанавливаем приоритет can_set_id(&TXRXBaEIDL, id, ext); //Установка маски передачи TXBaDLC = len; //Инициализируем счетчик передачи данных TXBaDLC.rtr = rtr; for (i=0; i<len; i++) { *txd0 = *data; txd0++; data++; } TXBaCON.txreq = 1; //Разрешаем передачу CANCON.win = CAN_WIN_RX0; return(ret); } /* Функция can__getd() извлекает данные из буфера приема, если они существуют. Возвращает в параметрах: id - ID отправившего сообщение; data - указатель на массив данных; len - длину массива принятых данных; stat - структуру, содержащую служебную информацию (буфер, принявший данные; тип ID и т.д.) Функция возвращает TRUE, если в буфере приема есть данные */ inti can_getd(long int & id, int * data, int & len, //inti = bit struct rx_stat & stat) { int i; int * ptr; if (RXBOCON.rxful) { CANCON.win = CAN_WIN_RX0; stat.buffer = 0; CAN_INT_RXB0IF = 0; stat.err_ovf1 = COMSTAT.rxOovf1; COMSTAT.rxOovfl = 0; if (RXBOCON.rxbOdben) stat.filthit = RXBOCON.filthitO;
Обмен данными по шине CAN 211 Листинг 5.8. Окончание } else if (RXB1CON.rxful) { CANCON.win = CAN_WIN_RX1; stat.buffer = 1; CAN_INT_RXB1IF = 0; stat, er r__o vf 1 = COMS TAT . rx 1 о vf 1; COMSTAT.rxlovfl = 0; stat.filthit = RXB1CON.filthit; } else return (0) ; len = RXBaDLC.dlc; stat.rtr = RXBaDLC.rtr; stat.ext = TXRXBaSIDL.ext; id = can_get_id(&TXRXBaEIDL, stat.ext); ptr = &TXRXBaD0; for (i=0; i<len; i++) { *data = *ptr; data++; ptr++; } CANCON.win = CAN_WIN_RX0; //Возврат к адресации по умолчанию stat.inv = CAN_INT_IRXIF; CAN_INT_IRXIF = 0; if (stat.buffer) RXB1CON.rxful = 0; else RXBOCON.rxful = 0; return(1); Листинг 5.9. Файл canbus. h #ifndef __CCS_CAN_LIB_DEFINES__ #define __CCS_CAN_LIB_DEFINES__ #IFNDEF CAN_USE_EXTENDED_ID #define CAN_USE_EXTENDED_ID TRUE #ENDIF #IFNDEF CAN_BRG_SYNCH_JUMP_WIDTH #define CAN_BRG_SYNCH_JUMP_WIDTH 0 #ENDIF #IFNDEF CAN_BRG_PRESCALAR #define CAN_BRG_PRESCALAR 8 #ENDIF #ifndef CAN_BRG_SEG_2_PHASE_TS #define CAN_BRG_SEG_2_PHASE_TS TRUE #endif 14*
212 Глава 5. Программные примеры для микроконтроллеров PIC Листинг 5.9. Продолжение tfifndef CAN_BRG_SAM # define CAN_BRG_SAM о #endif #ifndef C7kN_BRG_PHASE_SEGMENT_l # define CAN_BRG_PHASE_SEGMENT_1 5 #endif tfifndef CAN_BRG_PROPAGATION_TIME tfdefine CAN_BRG_PROPAGATION_TIME 2 tfendif ttifndef CAN_BRG_WAKE_FILTER # define CAN_BRG_WAKE_FILTER FALSE #endif tfifndef CAN_BRG_PHASE_SEGMENT_2 # define CAN_BRG_PHASE_SEGMENT_2 5 #endif #ifndef CAN_USE_RX_DOUBLE_BUFFER #define CAN USE RX DOUBLE BUFFER FALSE #endif tfifndef CAN_ENABLE_DRIVE_HIGH #define CAN_ENABLE_DRIVE_HIGH 0 tfendif #ifndef CAN_ENABLE_CAN_CAPTURE # define CAN_ENABLE_CAN__CAPTURE 0 #endif enum CAN_OP_MODE {CAN_OP_CONFIG=4, CAN_OP_LISTEN=3, CAN_OP_LOOPBACK=2, CAN_OP_DISABLE=1, CAN_OP_NORMAL=0}; enum CAN_WIN_ADDRESS {CAN_WIN_RXO=O, CAN_WIN_RX1=5, CAN_WIN__TX0=4 , CAN_WIN_TX1=3, CAN_WIN_TX2=2}; struct { inti voidO; //0 CAN_WIN_ADDRESS win:3; //1:3 - разряды адреса окна inti abat; //4 - прервать все незавершенные передачи CAN_OP_MODE reqop:3; //5:7 - разряды режима работы CAN } CANCON; #byte CANCON = 0xF6F enum CAN_INT_CODE {CAN_INT_WAKEUP=7 , CAN_INT_RX0=6 , CAN INT RX1=5, CAN INT TX0=4, CAN INT TX1=3,
Обмен данными по шине CAN 213 Листинг 5.9. Продолжение CAN_INT_TX2=2, CAN_INT_ERROR=1, CAN_INT_NO=0}; //Регистр состояния CAN (только для чтения) struct { inti voidO; //О CAN_INT_CODE icode:3; //1:3 - код прерывания inti void4; //4 CAN__OP_MODE opmode: 3; //5:7 - состояние рабочего режима } CANSTAT; #byte CANSTAT = 0xF6E //Регистр состояния омбена данными (только для чтения) struct { inti ewarn; //0 - флаг ошибки inti rxwarn; //1 - уведомление от приемника inti txwarn; //2 - уведомление от передатчика inti rxbp ; //3 - шина приемника пассивна inti txbp ; //4 - шина передатчика пассивна inti txbo ; //5 - шина передатчика отключена inti rxlovf1; //6 - переполнение буфера приема 1 inti rxOovf1; //7 - переполнение буфера приема 1 } COMSTAT; #byte COMSTAT=OxF74 #byte COMSTATUS=OxF74 //Регистр управления скоростью передачи 1 struct { int brp:6; //0:5 - коэффициент деления скорости передачи int sjw:2; //6:7 - ширина синхронизированного перехода } BRGCON1; #byte BRGCONl=0xF70 //Регистр управления скоростью передачи 2 struct { int prseg:3; //0:2 - выбор времени прохождения int seglph:3; //3:5 - фазовый сегмент 1 inti sam; //6 - стробирование линии шины CAN inti seg2phts; //7 — выбор времени для фазового сегмента 2 } BRGCON2; #byte BRGCON2=OxF71 //Регистр управления скоростью передачи 3 struct { int seg2ph:3; //0:2 - выбор времени для фазового сегмента 2 int void543:3; //3:5 inti wakfil; //6 - выбор фильтра линии для активизации шины inti void7; //7 } BRGCON3; #byte BRGCON3=OxF72
214 Глава 5. Программные примеры для микроконтроллеров PIC Листинг 5.9. Продолжение //Регистр struct { управления вводом-выводом int void3210:4; //0:3 inti cancap; //4 - захват приема сообщения inti endrhi; //5 - enable drive high int void7 6:2; } CIOCON; #byte CIOCON=OxF73 //6:7 //Регистр управления буфера передачи n struct txbNcon struct { int txpri:2; //0:1 - разряды приоритетности передачи inti void2; //2 inti txreq; //3 - состояние запроса на передачу // (сбрасывается в 0 для обрыва сообщения) inti txerr; //4 - флаг ошибки при передаче inti txlarb; //5 - состояние арбитража передачи inti inti }; txabt; void7; //6 - состояние прерывания передачи struct txbNcon_struct TXBOCON; struct txbNcon_struct TXB1CON; struct txbNcon_struct TXB2CON; struct txbNcon_struct TXBaCON; #byte TXB0CON=0xF40 #byte TXBlCON=0xF30 #byte TXB2CON=0xF20 #byte TXBaCON=0xF60 #byte TXB0CONR=0xF40 #byte TXBlCONR=0xF30 #byte TXB2CONR=0xF20 //Стандартный идентификатор буфера передачи #byte TXB0SIDH=0xF41 #byte TXB0SIDL=0xF42 #byte TXBlSIDH=0xF31 #byte TXBlSIDL=0xF32 #byte TXB2SIDH=0xF21 #byte TXB2SIDL=0xF22 //Расширенный идентификатор буфера передачи #byte TXB0EIDH=0xF43 #byte TXB0EIDL=0xF44 #byte TXBlEIDH=0xF33 #byte TXBlEIDL=0xF34 #byte TXB2EIDH=0xF23 #byte TXB2EIDL=0xF24
Обмен данными по шине CAN 215 Листинг 5.9. Продолжение //Байт данных m буфера передачи п #byte TXB0D0=0xF46 #byte TXB0D7=0xF4D #byte TXBlD0=0xF36 #byte TXBlD7=0xF3D #byte TXB2D0=0xF26 #byte TXB2D7=0xF2D //Длина данных в буфере передачи п struct txbNdlc_struct { int die:4; //0:3 int void54:2; //4:5 inti rtr; //6 inti void7; //7 } ; struct txbNdlc_struct TXBODLC; struct txbNdlc_struct TXB1DLC; struct txbNdlc_struct TXB2DLC; struct txbNdlc_struct TXBaDLC; #byte TXB0DLC=0xF45 #byte TXBlDLC=0xF35 #byte TXB2DLC=0xF25 #byte TXBaDLC=0xF65 //Регистр количества ошибок передачи #byte TXERRCNT=0xF76 enum CAN_RX_MODE {CAN_RX_ALL=3, CAN_RX_EXT=2, CAN_RX_STD=1, CAN_RX_VALID=0}; //Регистр управления буфером приема 0 struct { inti filthitO; //0 - обращение к фильтру inti jtoff; //1 - смещение в таблице переходов inti rxbOdben; //2 - разрешение двойного буфера char rxrtrro; //3 - принимать запрос на удаленную передачу char void4; //4 CAN_I <X_MODE rxm :2; //5:6 - режим буфера приема char rxful; //7 - состояние полноты приема } RXBOCON; #byte RXB0CON=0xF60 //Регистр управления буфером приема 1 struct { int filthit:3; //0:2 char rxrtrro; //3 - принимать запрос на удаленную передачу char void4; //4 CAN RX MODE rxm:2; //5:6 - режим буфера приема
216 Глава 5. Программные примеры для микроконтроллеров PIC Листинг 5.9. Продолжение char rxful; //7 - буфер заполнен } RXB1CON; #byte RXBlCON=0xF50 //Стандартный идентификатор буфера приема п #byte RXB0SIDH=0xF61 #byte RXB0SIDL=0xF62 #byte RXBlSIDH=0xF51 #byte RXBlSIDL=0xF52 //Расширенный идентификатор буфера приема n #byte RXB0EIDH=0xF63 #byte RXB0EIDL=0xF64 #byte RXBlEIDH=0xF53 #byte RXBlEIDL=0xF54 #byte TXRXBaEIDL=0xF64 struct { int void012:3; //0:3 inti ext; //Расширенный ID inti srr; //Разряд "substitute remove request" int void567:3; //5:7 } TXRXBaSIDL; #byte TXRXBaSIDL=0xF62 //Регистр кода длины данных буфера приема п struct rxbNdlc_struct { int die:4; //0:3 - код длины данных char rbO; //4 - зарезервирован char rbl; //5 - зарезервирован char rtr; //6 - разряд "remote transmission request" char void7; //7 }; struct rxbNdlc_struct RXBODLC; struct rxbNdlc_struct RXB1DLC; struct rxbNdlc_struct RXBaDLC; #byte RXB0DLC=0xF65 #byte RXBlDLC=0xF55 #byte RXBaDLC=0xF65 //Регистр поля данных байта m буфера приема п #byte RXB0D0=0xF66 #byte RXB0D7=0xF6D #byte RXBlD0=0xF56 #byte RXBlD7=0xF5D #byte TXRXBaD0=0xF66 #byte TXRXBaD7=0xF6D
Обмен данными по шине CAN 217 Листинг 5.9. Продолжение //Счетчик ошибок приема #byte RXERRCNT=0xF75 //Стандартный идентификатор фильтра принятия приема п #byte RXFOSIDH=OxFOO #byte RXF0SIDL=0xF01 #byte RXFlSIDH=0xF04 #byte RXFlSIDL=0xF05 #byte RXF2SIDH=0xF08 #byte RXF2SIDL=0xF09 #byte RXF3SIDH=0xF0C #byte RXF3SIDL=0xF0D #byte RXF4SIDH=0xF10 #byte RXF4SIDL=0xFll #byte RXF5SIDH=0xF14 #byte RXF5SIDL=0xF15 //Расширенный идентификатор фильтра принятия приема п #byte RXF0EIDH=0xF02 #byte RXF0EIDL=0xF03 #byte RXFlEIDH=0xF06 #byte RXFlEIDL=0xF07 #byte RXF2EIDH=0xF0A #byte RXF2EIDL=0xF0B #byte RXF3EIDH=0xF0E #byte RXF3EIDL=0xF0F #byte RXF4EIDH=0xF12 #byte RXF4EIDL=0xF13 #byte RXF5EIDH=0xF16 #byte RXF5EIDL=0xF17 //Маска стандартного идентификатора маски принятия приема п #byte RXM0SIDH=0xF18 #byte RXM0SIDL=0xF19 #byte RXM1SIDH=0xF1C #byte RXM1SIDL=0xF1D //Значение, помещаемое в поле маски для принятия всех входящих ID #define CAN_MASK_ACCEPT_ALL О //Маска расширенного идентификатора маски принятия приема п #byte RXMOEIDH=OxFlA #byte RXMOEIDL=OxFlB #byte RXMlEIDH=OxFlE #byte RXM1EIDL=0xF1F //Флаги прерываний CAN #bit CAN__INT_IRXIF = 0xFA4.7 #bit CAN INT WAKIF = 0xFA4.6
218 Глава 5. Программные примеры для микроконтроллеров PIC Листинг 5.9. Окончание # bit CAN_INT_ERRIF = 0xFA4.5 # bit CAN_INT_TXB2IF = 0xFA4.4 # bit CAN_INT_TXB1IF = 0xFA4.3 # bit CAN_INT_TXBOIF = 0xFA4.2 # bit CAN_INT_RXB1IF = 0xFA4.1 # bit CAN_INT_RXBOIF = 0xFA4.0 struct rx_stat { inti err_ovf1; int filthit:3; inti buffer; inti rtr; inti ext; inti inv; } ; void can__init (void) ; void can_set_baud(void) ; void can_set_mode(CAN_OP_MODE mode); void can_set_id(int* addr, long int id, inti ext); long int can_get_id(int * addr, inti ext) ; int can_putd(long int id, int * data, int len, int priority, inti ext, inti rtr); inti can_getd(long int & id, int * data, int & len, struct rx_stat & stat); #define can_kbhit() (RXBOCON.rxful || RXB1CON.rxful) #define can_tbe() (’TXBOCON. txreq | | !TXB1CON.txreq I I ’TXB2CON.txreq) #define can_abort() (CANCON.abat=l) #endif Листинг 5.10. Файл CAN.C #include <18F458.h> #include Ccanbus.c> //Подключение библиотеки функций CAN #use delay(clock=10000000) #fuses HS, BROWNOUT, WRT, NOWDT, WDT1 #use fast_io(В) //Для непосредственного ввода-вывода через порт В #use rs232(baud=19200,parity=N,xmit=PIN_C6,rcv=PIN_C7) int ms; //Счетчик миллисекунд struct rx_stat rxstat; //Структура для хранения состояния CAN //Объявление переменных, необходимых для хранения принятой по //шине CAN служебной информации и данных int32 rx__id; //CAN ID int in_data[8J; //Массив принятых данных int rx len; //Длина принятых данных в байтах
Обмен данными по шине CAN 219 Листинг 5.10, Продолжение //Объявление переменных, необходимых для хранения передаваемой по //шине CAN служебной информации и данных int out data[8]; //Массив передаваемых данных long int tx_id = 24; //Передача по CAN ID 24 inti tx rtr = 0; //inti - встроенный тип данных CCS (bit) inti tx_ext = 0; int tx_len = 8; //Передача 8 байт int tx_pri = 3; #int_timer2 //Обработка прерывания при переполнении TMR2 void isr_timer2(void) { ms++; //Увеличение счетчика каждую миллисекунду } void main () { int i, j ; setup_adc_ports(NO_ANALOGS); //Аналоговый интерфейс отключен setup_adc (ADC_CLOCK_DIV_2) ; //Режим работы АЦП setup_psp(PSP_DISABLED); //Режим PSP отключен setup_spi(FALSE); //Интерфейс SPI отключен setup_wdt(WDT_OFF); //Сторожевой таймер отключен setup__timer_0 (RTCC_INTERNAL) ; //Тактирование TMRO - внутреннее setup_timer_l(Tl_disabled); //Таймер TMR1 отключен setup_timer_3 (T3_INTERNAL | T3_DIV_BY_1) ; //TMR3 без делителя setup_ccpl (CCP_PWM) ; //CCP1 работает в режиме ШИМ setup_ccp2(CCP_PWM); //CCP2 работает в режиме ШИМ setup_comparator(FALSE); //Аналоговый компаратор отключен for (i=0; i<8; i++) //Очищаем массивы передаваемых { //и принимаемых данных out_data[i] = 0; in_data[i] =0; } //Инициализируем таймер TMR2 таким образом, чтобы прерывание //при переполнении возникало каждые 2 мс setup_timer_2(T2_DIV_BY_4, 79, 16); can_init(); //Инициализация модуля шины CAN enable_interrupts(INT_TIMER2); //Разрешаем прерывание от TMR2 enable_interrupts(global); //Общее разрешение прерываний while(1) //Начинаем цикл взаимодействия с шиной CAN { if (can_kbhit()) //Если по шине CAN были приняты данные,... { //Пытаемся извлечь информацию из буфера приема в переменные if (can_getd(rx_id, in_data, rx_len, rxstat)) { //В случае успеха выводим через USART отчет о состоянии printf("\r\nGot: Buff=%U ID=%LU Len=%U Ovf=%U ", rxstat.buffer, rx id, rx len, rxstat.err ovfl);
220 Глава 5. Программные примеры для микроконтроллеров PIC Листинг 5.10. Окончание printf("Filt=%U RTR=%U Ext=%U Inv=%U", rxstat.filthit, rxstat.rtr, rxstat.ext, rxstat.inv); printf("\r\n Data = "); //Выводим принятые данные for (i=0; i<rx_len; i++) printf("%X ", in_data[i]); printf("\r\n"); } else //Если извлечь информацию из буфера приема не удалось, //выводим сообщение об ошибке printf("\r\nFail on getting data\r\n"); } //Каждую секунду передаем данные, если буфер передачи пуст if (can_tbe() && (ms > 500)) { ms = 0; //Сбрасываем счетчик для отсчета следующей секунды //Записываем в массив передаваемых данных очередные 8 чисел for (i=0; i<8; i++) out_data[i] = j++; //Пытаемся поместить данные в буфер передачи шины CAN i = can_putd (tx_id, out_data, tx__len, tx_pri , tx_ext, tx_rtr) ; if (i >= OxFF) //Если данные успешно помещены в буфер,... { //Выводим через UART отчет о состоянии передачи printf("\r\nPut %U: ID=%LU Len=%U ", i, tx_id, tx_len); printf("Pri=%U Ext=%U RTR=%U\r\n Data = ", tx_pri, tx_ext, tx_rtr); //Выводим информацию о переданных данных for (i=0; i<tx_len; i++) printf("%X ", out_data[i]); printf("\r\n"); } //Если при попытке поместить данные в буфер передачи //возникла ошибка, передаем через USART сообщение об этом else printf("\r\nFail on putting data\r\n"); } else //Если в течение 2-х секунд не принято и не передано { //никаких данных, . . . if (ms > 1000) { //Выдаем отчет о состоянии шины CAN printf("\r\n\nCOMSTAT = %02Х", COMSTATUS); printf("\r\nTXB0CON = %02Х", TXB0CONR); printf("\r\nTXBlCON = %02Х", TXB1CONR); printf("\r\nTXB2CON = %02Х", TXB2CONR); printf("\r\nRXERRCNT = %U", RXERRCNT); printf("\r\nTXERRCNT = %U", TXERRCNT); ms = 0; } } } }
Обмен данными по интерфейсу I2C 221 Обмен данными по интерфейсу 12С Два микроконтроллера PIC соединены по интерфейсу 12С (линиям SDA и SCL могут, к примеру, соответствовать выводы 3 и 4 порта С). Ведущий микрокон- троллер периодически принимает данные через порт В и передает их ведомому микроконтроллеру, который отображает их с помощью светодиодов, подключен- ных к выводам порта D. Затем ведущий микроконтроллер принимает данные, по- лученные от ведомого, и также отображает их с помощью светодиодов, подклю- ченных к выводам порта D. Программа для ведущего микроконтроллера пред- ставлена в листинге 5.11, а для ведомого — в листинге 5.12. Файлы Для этого примера I2C_M. с, I2C_S . с, I2C_M.pj t и I2C_S . pj t также находятся ’•IK, ' на прилагаемом к книге компакт-диске в папке Projects\PIC\I2C. Листинг 5.11. Файл 12С_м. а #include <16F877.h> #use delay(clock=lО 000000) # fuses HS, WDT //Используется сторожевой таймер //Инициализируем интерфейс I2C в режиме Master (ведущий) # use i2c (Master , sda=PIN_C4 , scl=PIN_C3 , RESTART_WDT , FORCE_HW) # int_timerl //Обработка прерывания при переполнении TMR1 timerl__isr () { i2c_start(); //Инициализация передачи данных по I2C i2c_write(0хА0); //Передаем ведомому адрес с запросом на запись i2c_write(input_b()); //Записываем в ведомый микроконтроллер //данные, принятые через порт В i2c_start(); //Опять инициализируем передачу i2c_write(0хА1); //Передаем адрес с запросом на чтение output_D(~i2c_read(0)); //Считываем данные их ведомого //микроконтроллера без квитирования и отображаем их i2c_stop(); //Останов передачи данных 1 void main() { //Инициализируем сброс от сторожевого таймера через 1152 мс setup_counters (RTCC_INTERNAL, WDT_1152MS) ; setup_timer_l (T1_INTERNAL | T1_DIV_BY_4); //TMR1 без делителя //Активизируем внутренние подтягивающие резисторы для порта В port_b_pullups(TRUE); enable_interrupts(INT_TIMER1); //Разрешаем прерывание от TMR1 enable_interrupts(global)/ //Общее разрешение прерываний while(1) //Бесконечный цикл в ожидании прерывания от таймера { restart__wdt () ; //Используем сторожевой таймер, если } //интерфейс I2C "завис" 1
222 Глава 5. Программные примеры для микроконтроллеров PIC Листинг 5.12. Файл i2C_s. с ttinclude <16F877.h> #use delay(clock=10000000) tffuses HS, NOWDT //Инициализируем интерфейс I2C в режиме Slave (ведомый) #use i2c(Slave,sda=PIN_C4,scl=PIN_C3,address=0xA0) enum {address, data}; //Определяем перечислимый тип для //идентификации текущего режима Slave: //address - прием адреса; data - обмен данными int mode; //Текущий режим #int_SSP //Обработка прерывания при запросе от Master SSP_isr() { int с; if (i2cj3oll()) //Если в буфере приема есть байт данных,... { с = i2c_read(); //считываем его if (mode =— address) //Если текущий режим - прием адреса,... mode = data; //меняем его на режим обмена данными else //Если текущий режим - прием данных,... { output_D(~с); //Отображаем принятые данные с помощью //светодиодов, подключенных к выводам порта D mode = address; //Переходим в режим приема адреса } } else i2c_write(input_b()); //Если буфер приема пуст, то это //говорит о том, что Master выдал запрос на прием, //и Slave передает данные, принятые через порт В } void main () { //Активизируем внутренние подтягивающие резисторы для порта В port_b_pullups(TRUE); enable_interrupts(INT_SSP); //Разрешаем прерывание от SSP enable_interrupts(global); //Общее разрешение прерываний while(1); //Бесконечный цикл в ожидании запросов от Master } Индикация уровня напряжения на аналоговом входе На аналоговый вход AN0 микроконтроллера подается напряжение в диапазо- не 0—4 В. Требуется организовать индикацию текущего уровня напряжения с по- мощью восьми светодиодов, подключенных к выводам порта D: если напряжение <0,5 В, то включен светодиод на выводе 0; диапазону 0,5-1 В соответствуют све- тодиоды на выводах 0 и 1; диапазону 1-1,5 В — на выводах 0-2 и т.д.
Индикация уровня напряжения на аналоговом входе 223 Будем использовать 10-тиразрядное аналого-цифровое преобразование с ко- эффициентом деления частоты системной синхронизации для тактирования АЦП, равным 32. Число, полученное в результате преобразования, которому соответст- вует “деление” 0,5 В, определяем из соотношения Uin/Ufuii = Х/(2П - 1), где п — разрядность преобразования. Отсюда получаем X = 1023 • 0,5 / 5 = 102,3. Таким образом, принимаем, что 0,5 В соответствует число 102. Программа, реализующая этот алгоритм, представлена в листинге 5.13. ©Файлы для этого примера ULevel. с и ULevel. pj t также находятся на прилагаемом к кни- ге компакт-диске в папке Pro jects\PIC\ULevel. Листинг 5.13. Файл ULevel. С tfinclude <18F458.h> //Эта директива необходима для корректной работы функции read_adc #device ADC=10 //Устанавливаем разрешение 10 разрядов #use delay(clock=10000000) tffuses HS, NOWDT #byte ADCONO = OxlF //Определяем регистр управления АЦП int n; #int_AD //Обработка прерывания от АЦП AD_isr () { n = read_adc() / 102; /* Определяем количество включаемых светодиодов. Если п=0, то горит светодиод на выводе 0. Если п=1, то горят светодиоды на выводах 0и1ит.д. */ output_D (*-(2 « п - 1) ) ; /* Выражению 2 << п соответствует умножение 2 на 2 п раз (сдвиг влево на п разрядов). Тогда, если п = 0 (U < 0,5 В), то 2 « п - 1 = 2 - 1 = 1; (00000001) если n = 1 (U = 0,5-1 В), то 2 « п - 1 = 4 - 1 = 3; (00000011) В порт D выдается инвертированный результат (т.е. 11111110, 11111100 и т.д.) для включения соответствующих светодиодов */ ADCONO |= 4; //Возобновляем аналого-цифровое преобразование 1 //установкой в регистре управления разряда GO void main () { setup_adc_ports(ALL_ANALOG); //Используем внутренний источник //опорного напряжения. Все выводы порта А - аналоговые входы setup_adc(ADC_CLOCK_DIV_32); //Тактирование АЦП с Fosc/32 enable_interrupts(INT_AD); //Разрешаем прерывание от АЦП enable_interrupts(global); //Общее разрешение прерываний set_adc_channel(0); //Выбираем для АЦП канал 0 ADCONO |= 4; //Начинаем аналого-цифровое преобразование //установкой в регистре управления разряда GO while(l); //Бесконечный цикл в ожидании прерывания от АЦП }
Приложение A Таблица символов ASCII Сим- вол Коди[ ювка Сим- вол Кодировка Сим- вол Кодировка Сим- вол Кодис )овка Dec | Hex Dec Hex Dec Hex Dec 1 Hex (nul) 0 0x00 (sp) 32 0x20 @ 64 0x40 96 0x60 (soh) 1 0x01 1 33 0x21 А 65 0x41 а 97 0x61 (stx) 2 0x02 n 34 0x22 В 66 0x42 ь 98 0x62 (etx) 3 0x03 # 35 0x23 С 67 0x43 с 99 0x63 (eot) 4 0x04 $ 36 0x24 D 68 0x44 d 100 0x64 (enq) 5 0x05 % 37 0x25 Е 69 0x45 е 101 0x65 (ack) 6 0x0 6 & 38 0x2 6 F 70 0x4 6 f 102 0x66 (bel) 7 0x07 39 0x27 G 71 0x47 g 103 0x67 (bs) 8 0x08 ( 40 0x28 Н 72 0x48 h 104 0x68 (ht) 9 0x09 ) 41 0x29 I 73 0x49 i 105 0x69 (nl) 10 OxOA * 42 0x2A J 74 0х4А j 106 0x6A (vt) 11 0x0В 43 0x2B К 75 0x4В k 107 0x6B (np) 12 OxOC t 44 0x2C L 76 0х4С 1 108 0x6C (cr) 13 OxOD - 45 0x2D М 77 0x4D m 109 0x6D (so) 14 OxOE 46 0x2E N 78 0х4Е n 110 0x6E (si) 15 OxOF / 47 0x2F 0 79 0x4F о 111 0x6F (die) 16 0x10 0 48 0x30 Р 80 0x50 p 112 0x70 (del) 17 0x11 1 49 0x31 Q 81 0x51 q 113 0x71 (dc2) 18 0x12 2 50 0x32 R 82 0x52 r 114 0x72 (dc3) 19 0x13 3 51 0x33 S 83 0x53 s 115 0x73 (de4) 20 0x14 4 52 0x34 Т 84 0x54 t 116 0x74 (nak) 21 0x15 5 53 0x35 и 85 0x55 u 117 0x7 5 (syn) 22 0x16 6 54 0x3 6 V 86 0x5 6 V 118 0x7 6 (etb) 23 0x17 7 55 0x37 W 87 0x57 w 119 0x77 (can) 24 0x18 8 56 0x38 X 88 0x58 X 120 0x78 (em) 25 0x19 9 57 0x39 Y 89 0x59 У 121 0x7 9 (sub) 26 OxlA 58 ОхЗА Z 90 0х5А z 122 0x7A (esc) 27 OxlB 59 ОхЗВ [ 91 0x5В { 123 0x7B (fs) 28 OxlC < 60 ОхЗС \ 92 0х5С 1 124 0x7C (gs) 29 OxlD = 61 0x3D ] 93 0x5D } 125 0x7D (rs) 30 OxlE > 62 ОхЗЕ А 94 0х5Е 126 0x7E (us) 31 OxlF 2 63 ОхЗЕ — 95 0x5F (del) 127 0x7F
Приложение Б Преобразование из одной системы счисления в другую Dec Bin Oct Hex 0 00000000 000 00 1 00000001 001 01 2 00000010 002 02 3 00000011 003 03 4 00000100 004 04 5 00000101 005 05 6 00000110 006 06 7 00000111 007 07 8 00001000 010 08 9 00001001 Oil 09 10 00001010 012 0A 11 00001011 013 0B 12 00001100 014 ОС 13 00001101 015 0D 14 00001110 016 0E 15 00001111 017 OF 16 00010000 020 10 17 00010001 021 11 18 00010010 022 12 19 00010011 023 13 20 00010100 024 14 21 00010101 025 15 22 00010110 026 16 23 00010111 027 17 24 00011000 030 18 25 00011001 031 19 26 00011010 032 1A 27 00011011 033 IB 28 00011100 034 1C 29 00011101 035 ID 30 00011110 036 IE 31 00011111 037 IF 32 00100000 040 20 33 00100001 041 21 34 00100010 042 22 35 00100011 043 23 Dec Bin Oct Hex 36 00100100 044 24 37 00100101 045 25 38 00100110 046 26 39 00100111 047 27 40 00101000 050 28 41 00101001 051 29 42 00101010 052 2A 43 00101011 053 2B 44 00101100 054 2C 45 00101101 055 2D 46 00101110 056 2E 47 00101111 057 2F 48 00110000 060 30 49 00110001 061 31 50 00110010 062 32 51 00110011 063 33 52 00110100 064 34 53 00110101 065 35 54 00110110 0 6 6 36 55 00110111 067 37 56 00111000 070 38 57 00111001 071 39 58 00111010 072 ЗА 59 00111011 073 ЗВ 60 00111100 074 ЗС 61 00111101 075 3D 62 00111110 076 ЗЕ 63 00111111 077 3F 64 01000000 100 40 65 01000001 101 41 66 01000010 102 42 67 01000011 103 43 68 01000100 104 44 69 01000101 105 45 70 01000110 106 46 71 1 01000111 107 47 15-6-767
226 Приложение Б. Преобразование из одной системы счисления в другую Dec Bin Oct Hex 72 01001000 110 48 73 01001001 111 49 74 01001010 112 4A 75 01001011 113 4B 76 01001100 114 4C 77 01001101 115 4D 78 01001110 116 4E 79 01001111 117 4F 80 01010000 120 50 81 01010001 121 51 82 01010010 122 52 83 01010011 123 53 84 01010100 124 54 85 01010101 125 55 86 01010110 126 56 87 01010111 127 57 88 01011000 130 58 89 01011001 131 59 90 01011010 132 5A 91 01011011 133 5B 92 01011100 134 5C 93 01011101 135 5D 94 01011110 136 5E 95 01011111 137 5F 96 01100000 140 60 97 01100001 141 61 98 01100010 142 62 99 01100011 143 63 100 01100100 144 64 101 01100101 145 65 102 01100110 146 66 103 01100111 147 67 104 01101000 150 68 105 01101001 151 69 106 01101010 152 6A 107 01101011 153 6B 108 01101100 154 6C 109 01101101 155 6D 110 01101110 156 6E 111 01101111 157 6F 112 01110000 160 70 113 01110001 161 71 114 01110010 162 72 115 01110011 163 73 116 01110100 164 74 117 01110101 165 75 118 01110110 166 76 Dec Bin Oct Hex 119 01110111 167 77 120 01111000 170 78 121 01111001 171 79 122 01111010 172 7A 123 01111011 173 7B 124 01111100 174 7C 125 01111101 175 7D 126 01111110 176 7E 127 01111111 177 7F 128 10000000 200 80 129 10000001 201 81 130 10000010 202 82 131 10000011 203 83 132 10000100 204 84 133 10000101 205 85 134 10000110 206 86 135 10000111 207 87 136 10001000 210 88 137 10001001 211 89 138 10001010 212 8A 139 10001011 213 8B 140 10001100 214 8C 141 10001101 215 8D 142 10001110 216 8E 143 10001111 217 8F 144 10010000 220 90 145 10010001 221 91 146 10010010 222 92 147 10010011 223 93 148 10010100 224 94 149 10010101 225 95 150 10010110 226 96 151 10010111 227 97 152 10011000 230 98 153 10011001 231 99 154 10011010 232 9A 155 10011011 233 9B 156 10011100 234 9C 157 10011101 235 9D 158 10011110 236 9E 159 10011111 237 9F 160 10100000 240 A0 161 10100001 241 Al 162 10100010 242 A2 163 10100011 243 A3 164 10100100 244 A4 165 10100101 245 A5
227 Dec Bin Oct Hex 166 10100110 246 A6 167 10100111 247 A7 168 10101000 250 A8 169 10101001 251 A9 170 10101010 252 AA 171 10101011 253 AB 172 10101100 254 AC 173 10101101 255 AD 174 10101110 256 AE 175 10101111 257 AF 176 10110000 260 BO 177 10110001 261 Bl 178 10110010 262 B2 179 10110011 263 B3 180 10110100 264 B4 181 10110101 265 B5 182 10110110 266 B6 183 10110111 267 B7 184 10111000 270 B8 185 10111001 271 B9 186 10111010 272 BA 187 10111011 273 BB 188 10111100 274 BC 189 10111101 275 BD 190 10111110 276 BE 191 10111111 277 BF 192 11000000 300 CO 193 11000001 301 Cl 194 11000010 302 C2 195 11000011 303 C3 196 11000100 304 C4 197 11000101 305 C5 198 11000110 306 C6 199 11000111 307 C7 200 11001000 310 C8 201 11001001 311 C9 202 11001010 312 CA 203 11001011 313 CB 204 11001100 314 CC 205 11001101 315 CD 206 11001110 316 CE 207 11001111 317 CF 208 11010000 320 DO 209 11010001 321 DI 210 11010010 322 D2 Dec Bin Oct Hex 211 11010011 323 D3 212 11010100 324 D4 213 11010101 325 D5 214 11010110 326 D6 215 11010111 327 D7 216 11011000 330 D8 217 11011001 331 D9 218 11011010 332 DA 219 11011011 333 DB 220 11011100 334 DC 221 11011101 335 DD 222 11011110 336 DE 223 11011111 337 DF 224 11100000 340 E0 225 11100001 341 El 226 11100010 342 E2 227 11100011 343 E3 228 11100100 344 E4 229 11100101 345 E5 230 11100110 346 E6 231 11100111 347 E7 232 11101000 350 E8 233 11101001 351 E9 234 11101010 352 EA 235 11101011 353 EB 236 11101100 354 EC 237 11101101 355 ED 238 11101110 356 EE 239 11101111 357 EF 240 11110000 360 F0 241 11110001 361 Fl 242 11110010 362 F2 243 11110011 363 F3 244 11110100 364 F4 245 11110101 365 F5 246 11110110 366 F6 247 11110111 367 F7 248 11111000 370 F8 249 11111001 371 F9 250 11111010 372 FA 251 11111011 373 FB 252 11111100 374 FC 253 11111101 375 FD 254 11111110 376 FE 255 11111111 377 FF 15*
Приложение В Система команд микроконтроллеров AVR Ниже, в описании машинных команд микроконтроллеров AVR будут исполь- зованы некоторые условные обозначения. Перечислим их: С — флаг переноса (разряд 0 регистра SREG); Z — нулевой флаг (разряд 1 регистра SREG); N — флаг отрицательного результата (разряд 2 регистра SREG); V — флаг переполнения при вычислениях в дополнительных кодах (разряд 3 регистра SREG); S — флаг знака (разряд 4 регистра SREG); S = N ® V; Н — флаг половинного переноса (разряд 5 регистра SREG); Т — флаг копирования (разряд 6 регистра SREG); I — флаг общего разрешения прерываний (разряд 7 регистра SREG); Rd — регистр назначения в регистровом файле; Rr — передающий регистр; R — результат выполнения некоторой команды; Кп — n-битная константа (например, К8 = 1 байт); к — адресная константа для работы со счетчиком команд (PC); b — идентификатор некоторого разряда в рабочем регистре или в регистре ввода/вывода (3 бита); s — указывает на некоторый разряд в регистре состояния SREG; X, Y, Z — указатель при косвенной адресации (X = R27:R26, Y = R29:R28, Z = R3I:R30); RAMPZ, RAMPY, RAMPZ — регистры, связанные с указателями X, Y и Z для формирования косвенных адресов > 64 Кбайт; Rdl — младший байт регистровой пары Rdh:Rdl; Rdh — старший байт регистровой пары Rdh.Rdl; Р — адрес некоторого порта ввода/вывода; q — смещение при косвенной адресации; Стек — область памяти для хранения адреса возврата или регистр проме- жуточного хранения данных; SP — указатель вершины стека.
Алфавитный перечень команд 229 Алфавитный перечень команд ADC Название: Add With Carry Синтаксис: adc Rd, Rr Операция: Rd «— Rd + Rr + C Операнды: 0<d<31;0<r<31 Влияние на флаги: H. S, V, N, Z, C Описание: Содержимое регистра Rr и флаг переноса прибавляются к содержимому регистра Rd Тактовых циклов: 1 ADD Название: Add without Carry Синтаксис: add Rd, Rr Операция: Rd «— Rd + Rr Операнды: O<d<31;O<r<31 Влияние на флаги: H, S, V, N, Z, C Описание: Содержимое регистра Rr прибавляется к содержимому ре- гистра Rd Тактовых циклов: 1 ADIW Название: Add Immediate to Word Синтаксис: adiw Rdl, Кб Операция: Rdh:Rdl <— Rdh:Rdl + Кб Операнды: dl e {24, 26, 28, 30}; 0 < Кб < 63 Влияние на флаги: S, V, N, Z, С Описание: Константа Кб прибавляется к содержимому регистровой пары RdhiRdl. Эта команда работает с четырьмя самыми старшими регистровыми парами регистрового файла, и главным образом используется в операциях с указателями Тактовых циклов: 1 AND Название: Logical AND Синтаксис: and Rd, Rr Операция: Rd «— Rd a Rr Операнды: 0 <d < 31; 0< r< 31 Влияние на флаги: S, V = 0, N, Z Описание: Связывание логической операцией “И” содержимого реги- стров Rd и Rr. Результат записывается в регистр Rd Тактовых циклов: 1
230 Приложение В. Система команд микроконтроллеров AVR ANDI Название: Logical AND with Immediate Синтаксис: Операция: Операнды: Влияние на флаги: Описание: andi Rd, K8 Rd <- Rd л K8 0 < d< 31; 0<K8<255 S, V = 0, N, Z Связывание логической операцией “И” содержимого реги- стра Rd и 8гбитной константы К8. Результат записывается в регистр Rd Тактовых циклов: 1 ASR Название: Arithmetic Shift Right Синтаксис: Операция: Операнды: Влияние на флаги: Описание: as г Rd Rd(n)«—Rd(n+1), n = 0...6, C <- Rd(0) 0< Rd < 31 S, V, N, Z, C Разряды 0...6 регистра Rd сдвигаются на одну позицию вправо; разряд 0 сохраняется как флаг переноса; разряд 7 не изменяется. Эта команда может использоваться для де- ления на два байтов со знаковым разрядом без изменения этого разряда. Флаг переноса может использоваться для округления результата Тактовых циклов: 1 BCLR Название: Bit Clear in SREG Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: bclr s SREG(s) <- 0 0< s< 7 I, T, H, S, V, N, Z, C Сброс разряда s в регистре SREG 1 BLD Название: Bit load from Т-Flag in SREG to a Bit in Register Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: bld Rd, b Rd(b) «- T 0 <d < 31; 0<b<7 Нет Копирование флага T в разряд b регистра Rd 1
Алфавитный перечень команд 231 BRBC Название: Branch if Bit in SREG is cleared Синтаксис: Операция: Операнды: Влияние на флаги: Описание: brbc s, k7 Если SREG(s) = 0, to PC «— PC + k7 + 1, иначе PC «— PC +1 0 < s < 7; -64 < k7 < +63 Нет Если разряд s регистра SREG содержит лог. 0, то выполня- ется относительный переход по адресу в диапазоне от РС- 64 до РС+63 (слов) Тактовых циклов: 1 (условие не исполняется) или 2 (условие исполняется) BRBS Название: Branch if Bit in SREG is set Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Если SREG(s) = 1, to PC «— PC + k7 + 1, иначе PC <— PC +1 0 < s < 7; -64 < k7 < +63 Нет Если разряд s регистра SREG содержит лог. 1, то выполня- ется относительный переход по адресу в диапазоне от РС-64 до РС+63 (слов) Тактовых циклов: 1 (условие не исполняется) или 2 (условие исполняется) BRCC Название: Branch if Carry is cleared Синтаксис: Операция: Операнды: Влияние на флаги: Описание: brcc k7 Если флаг С = 0, то PC «— PC + k7 + 1, иначе PC «— PC + 1 -64 < k7 < +63 (дополнение до двух) Нет Если сброшен флаг переноса, то выполняется относитель- ный переход по адресу в диапазоне от РС-64 до РС+63 (слов) Тактовых циклов: 1 (флаг С = лог. 1) или 2 (флаг С = лог. 0) BRCS Название: Branch if Carry is set Синтаксис: Операция: Операнды: Влияние на флаги: Описание: brcs k7 Если флаг С = 1, то PC «— PC + k7 + 1, иначе PC <— PC + 1 -64 < k7 < +63 (дополнение до двух) Нет Если установлен флаг переноса, то выполняется относи- тельный переход по адресу в диапазоне от РС-64 до РС+63 (слов) Тактовых циклов: 1 (флаг С = лог. 0) или 2 (флаг С = лог. 1)
232 Приложение В. Система команд микроконтроллеров AVR BREQ Название: Branch if Equal Синтаксис: Операция: Операнды: Влияние на флаги: Описание: breq k7 Если флаг Z = 1, то PC «— PC + k7 + 1, иначе PC <— PC + 1 -64 < k7 < +63 (дополнение до двух) Нет Если установлен нулевой флаг, то выполняется относи- тельный переход по адресу в диапазоне от РС-64 до РС+63 (слов). Нулевой флаг после выполнения операции вычитания или сравнения устанавливается только в том случае, если результат равен 0x00 (то есть, содержимое обоих операндов равно) Тактовых циклов: 1 (флаг Z = лог. 0) или 2 (флаг Z = лог. 1) BRGE Название: Branch if Greater or Equal Синтаксис: Операция: Операнды: Влияние на флаги: Описание: brge k7 Если флаг S = 0, то PC «— PC + k7 + 1, иначе PC PC + 1 -64 < k7 < +63 (дополнение до двух) Нет Если сброшен флаг знака, то выполняется относительный переход по адресу в диапазоне от РС-64 до РС+63 (слов). Флаг знака после выполнения операции вычитания или сравнения сбрасывается только в том случае, если содер- жимое первого операнда со знаком больше или равно со- держимого второго операнда со знаком Тактовых циклов: 1 (флаг S = лог. 1) или 2 (флаг S = лог. 0) BRHC Название: Branch if Half Carry Flag is cleared Синтаксис: Операция: Операнды: Влияние на флаги: Описание: brhc k7 Если флаг H = 0, то PC PC + k7 + 1, иначе PC <— PC + 1 -64 < k7 < +63 (дополнение до двух) Нет Если сброшен флаг половинного переноса, то выполняется относительный переход по адресу в диапазоне от РС-64 до РС+63 (слов) Тактовых циклов: 1 (флаг Н = лог. 1) или 2 (флаг Н = лог. 0) BRHS Название: Branch if Half Carry Flag is set Синтаксис: Операция: Операнды: Влияние на флаги: brhs k7 Если флаг H = 1, то PC <— PC + k7 + 1, иначе PC «— PC + 1 -64 < k7 < +63 (дополнение до двух) Нет
Алфавитный перечень команд 233 Описание: Если установлен флаг половинного переноса, то выполня- ется относительный переход по адресу в диапазоне от РС- 64 до РС+63 (слов) Тактовых циклов: 1 (флаг Н = лог. 0) или 2 (флаг Н = лог. 1) BRID Название: Branch if Global Interrupt is Disabled Синтаксис: Операция: Операнды: Влияние на флаги: Описание: brid k7 Если флаг I = 0, то PC PC + k7 + l, иначе PC «— PC + 1 -64 < k7 < +63 (дополнение до двух) Нет Если сброшен флаг I (все прерывания запрещены), то вы- полняется относительный переход по адресу в диапазоне от РС-64 до РС+63 (слов) Тактовых циклов: 1 (флаг I = лог. 1) или 2 (флаг I = лог. 0) BRIE Название: Branch if Global Interrupt is Enabled Синтаксис: Операция: Операнды: Влияние на флаги: Описание: brie k7 Если флаг I = 1, то PC <— PC + k7 + 1, иначе PC «— PC + 1 -64 < k7 < +63 (дополнение до двух) Нет Если установлен флаг I (общее разрешение прерываний), то выполняется относительный переход по адресу в диапазоне от РС-64 до РС+63 (слов) Тактовых циклов: 1 (флаг I = лог. 0) или 2 (флаг I = лог. 1) BRLO Название: Branch if Lower Синтаксис: Операция: Операнды: Влияние на флаги: Описание: brio k7 Если флаг С = 1, то PC «— PC + k7 + 1, иначе PC PC + 1 -64 < k7 < +63 (дополнение до двух) Нет Если установлен флаг переноса, то выполняется относи- тельный переход по адресу в диапазоне от РС-64 до РС+63 (слов). Флаг переноса в результате выполнения операции вычитания или сравнения может быть установ- лен только в том случае, если содержимое первого беззна- кового операнда меньше, чем содержимое второго беззна- кового операнда Тактовых циклов: 1 (флаг С = лог. 0) или 2 (флаг С = лог. 1) BRLT Название: Branch if Less Then Синтаксис: brlt k7
234 Приложение В. Система команд микроконтроллеров AVR Операция: Операнды: Влияние на флаги: Описание: Если флаг S = 1, то PC <— PC + k7 + 1, иначе PC «— PC + 1 -64 < k7 < +63 (дополнение до двух) Нет Если установлен флаг знака, то выполняется относитель- ный переход по адресу в диапазоне от РС-64 до РС+63 (слов). Флаг знака в результате выполнения операции вы- читания или сравнения может быть установлен только в том случае, если содержимое первого операнда со зна- ком меньше, чем содержимое второго операнда со знаком Тактовых циклов: 1 (флаг S = лог. 0) или 2 (флаг S = лог. 1) BRMI Название: Branch if Minus Синтаксис: Операция: Операнды: Влияние на флаги: Описание: brmi k7 Если флаг N = 1, то PC «— PC + k7 + 1, иначе PC «— PC + 1 -64 < k7 < +63 (дополнение до двух) Нет Если установлен флаг отрицательного результата, то вы- полняется относительный переход по адресу в диапазоне от РС-64 до РС+63 (слов) Тактовых циклов: 1 (флаг N = лог. 0) или 2 (флаг N = лог. 1) BRNE Название: Branch if Not Equal Синтаксис: Операция: Операнды: Влияние на флаги: Описание: brne k7 Если флаг Z = 0, то PC «— PC + k7 + 1, иначе PC <— PC + 1 -64 < k7 < +63 (дополнение до двух) Нет Если сброшен нулевой флаг, то выполняется относитель- ный переход по адресу в диапазоне от РС-64 до РС+63 (слов). Нулевой флаг в результате выполнения операции вычитания или сравнения может быть сброшен только в том случае, если результат операции не равен 0x00 (то есть, содержимое двух операндов не равно) Тактовых циклов: 1 (флаг Z = лог. 1) или 2 (флаг Z = лог. 0) BRPL Название: Branch if Plus Синтаксис: Операция: Операнды: Влияние на флаги: Описание: brpl k7 Если флаг N = 0, то PC <— PC + k7 + 1, иначе PC «— PC + 1 -64 < k7 < +63 (дополнение до двух) Нет Если сброшен флаг отрицательного результата, то выпол- няется относительный переход по адресу в диапазоне от РС-64 до РС+63 (слов) Тактовых циклов: 1 (флаг N = лог. 1) или 2 (флаг N = лог. 0)
Алфавитный перечень команд 235 BRSH Название: Branch if Same or Higher Синтаксис: brsh k7 Операция: Если флаг С = 0, то PC «— PC + k7 + 1, иначе PC «— PC + 1 Операнды: -64 < k7 < +63 (дополнение до двух) Влияние на флаги: Нет Описание: Если сброшен флаг переноса, то выполняется относитель- ный переход по адресу в диапазоне от РС-64 до РС+63 (слов). Флаг переноса в результате выполнения операции вычитания или сравнения может быть сброшен только в том случае, если содержимое первого беззнакового опе- ранда больше или равно содержимого второго беззнаково- го операнда Тактовых циклов: 1 (флаг С = лог. 1) или 2 (флаг С = лог. 0) BRTC Название: Branch if Т-Flag is Cleared Синтаксис: brtc k7 Операция: Если флаг T = 0, то PC «— PC + k7 + 1, иначе PC <— PC + 1 Операнды: -64 < k7 < +63 (дополнение до двух) Влияние на флаги: Нет Описание: Если сброшен флаг Т, то выполняется относительный пе- реход по адресу в диапазоне от РС-64 до РС+63 (слов) Тактовых циклов: 1 (флаг Т = лог. 1) или 2 (флаг Т = лог. 0) BRTS Название: Branch if Т-Flag is Set Синтаксис: brts k7 Операция: Если флаг T = 1, то PC <— PC + k7 + 1, иначе PC <— PC + 1 Операнды: -64 < k7 < +63 (дополнение до двух) Влияние на флаги: Нет Описание: Если установлен флаг Т, то выполняется относительный переход по адресу в диапазоне от РС-64 до РС+63 (слов) Тактовых циклов: 1 (флаг Т = лог. 0) или 2 (флаг Т = лог. 1) BRVC Название: Branch if Overflow is Cleared Синтаксис: brvc k7 Операция: Если флаг V = 0, то PC «— PC + k7 + 1, иначе PC «— PC + 1 Операнды: -64 < k7 < +63 (дополнение до двух) Влияние на флаги: Нет Описание: Если сброшен флаг переполнения, то выполняется относи- тельный переход по адресу в диапазоне от РС-64 до РС+63 (слов) Тактовых циклов: 1 (флаг V = лог. 1) или 2 (флаг V = лог. 0)
236 Приложение В. Система команд микроконтроллеров AVR BRVS Название: Branch if Overflow is Set Синтаксис: Операция: Операнды: Влияние на флаги: Описание: brvs k7 Если флаг V = 1, то PC «— PC + k7 + I, иначе PC «— PC + 1 -64 < k7 < +63 (дополнение до двух) Нет Если установлен флаг переполнения, то выполняется от- носительный переход по адресу в диапазоне от РС-64 до РС+63 (слов) Тактовых циклов: 1 (флаг V = лог. 0) или 2 (флаг V = лог. 1) BSET Название: Bit Set in SREG Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: bset s SREG(s) 1 0< s< 7 I, T, H, S, V, N, Z, C Установка разряда s в регистре SREG 1 BST Название: Bit Store from Bit in Register to Т-Flag in SREG Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: bst Rd, b T Rd(b) 0 <d<31; 0<b<7 T Копирование в флаг T разряда b регистра Rd 1 CALL Название: Direct Subroutine Call Синтаксис: Операция: Операнды: Влияние на флаги: Описание: call kl6 (k22) PC<—kl6(k22) 0 < k < 64K (0 < k < 4M) Нет Вызов подрограммы по абсолютному адресу к16 (в микроконтроллерах с 16-разрядной шиной адреса) или к22 (в микроконтроллерах с 22-разрядной шиной адреса). Адрес возврата (также 16- или 22-разрядный) — команды, следующей после команды call — сохраняется в стеке Тактовых циклов: 4
Алфавитный перечень команд 237 свт Название: Clear Bit in I/O-Register Синтаксис: Операция: Операнды: cbi P, b I/O(P,b) <- 0 0<P<31; 0<b<7 Влияние на флаги: Описание: Нет Запись лог. 0 в разряд b регистра ввода/вывода Р. Эта ко- манда может применяться только для регистров ввода- вывода 0-31 (0x00-0x1 F) Тактовых циклов: 2 CBR Название: Clear Bit(s) in Register Синтаксис: Операция: Операнды: Влияние на флаги: Описание: cbr Rd, K8 Rd *— Rd a (OxFF - K8) 16 <d <31; 0 < K8 < 255 Нет Сброс в регистре Rd разрядов, заданных К8, с применени- ем логической операции “И” с дополненной константой К8. Команда применяется только для регистров г 16—гЗ 1 Тактовых циклов: 1 CLC Название: Clear Carry Flag Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: clc С <- 0 Нет C = 0 Сброс флага переноса в регистре SREG 1 CLH Название: Clear Half Carry Flag Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: clh H^-0 Нет H = 0 Сброс флага половинного переноса в регистре SREG 1 CLI Название: Clear Global Interrupt Flag Синтаксис: Операция: Операнды: cli 1^0 Нет
238 Приложение В. Система команд микроконтроллеров AVR Влияние на флаги: 1 = 0 Описание: Сброс флага общего разрешения прерываний в регистре SREG Тактовых циклов: 1 CLN Название: Clear Negative Flag Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: cln N «— 0 Нет N = 0 Сброс флага отрицательного результата в регистре SREG 1 CLR Название: Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Clear Register clr Rd «— Rd © Rd 0 < d < 31 S=0, V=0, N=0, Z=1 Благодаря применению логической операции “Исклю- чающее ИЛИ” содержимого регистра самому к себе, все разряды этого регистра устанавливаются в лог. 0 Тактовых циклов: 1 CLS Название: Clear Signed Flag Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: cis S<-0 Нет S = 0 Сброс флага знака в регистре SREG 1 CLT Название: Clear Transfer Flag Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: clt T«- 0 Нет Т = 0 Сброс флага копирования в регистре SREG 1
Алфавитный перечень команд 239 CLV Название: Clear Overflow Flag Синтаксис: civ Операция: V «— О Операнды: Нет Влияние на флаги: V = 0 Описание: Сброс флага переполнения в регистре SREG Тактовых циклов: 1 CLZ Название: Clear Zero Flag Синтаксис: clz Операция: Z «— О Операнды: Нет Влияние на флаги: Z = О Описание: Сброс нулевого флага в регистре SREG Тактовых циклов: 1 СОМ Название: One’s Complement Синтаксис: com Rd Операция: Rd <— OxFF - Rd Операнды: 0 < d < 31 Влияние на флаги: S, V = О, N, Z, С = 1 Описание: Дополнение до единицы содержимого регистра Rd Тактовых циклов: 1 СР Название: Compare Синтаксис: ср Rd, Rr Операция: Rd-Rr Операнды: 0 <d <31; 0 < г <31 Влияние на флаги: Н, S, V, N, Z, С Описание: Сравнение регистров Rd и Rr. При этом Rr вычитается из Rr без сохранения результата (содержимое Rd не изменя- ется). Флаги устанавливаются как при обычном вычита- нии, чтобы можно было проверить условие перехода Тактовых циклов: 1 СРС Название: Compare with Carry Синтаксис: срс Rd, Rr Операция: Rd-Rr-С Операнды: 0 <d <31; 0 <г<31 Влияние на флаги: Н, S, V, N, Z, С
240 Приложение В. Система команд микроконтроллеров AVR Описание: Содержимое регистров Rd и Rr сравнивается с помощью вычитания. При этом учитывается перенос, полученный в результате выполнения предыдущей операции. Содер- жимое регистров Rr и Rr не изменяется. Флаги можно ис- пользовать при условных переходах. Эта команда удобна для сравнения нескольких байтов Тактовых циклов: CPI Название: Compare with Immediate Синтаксис: Операция: Операнды: Влияние на флаги: Описание: cpi Rd, K8 Rd-K8 16<d<31; 0 < K8 < 255 H, S, V,N, Z, C Содержимое регистра Rd с помощью вычитания сравнива- ется с 8-разрядной константой К8. При этом содержимое регистра не изменяется. Флаги можно использовать при условных переходах Тактовых циклов: 1 CPSE Название: Compare Skip if Equal Синтаксис: Операция: Операнды: Влияние на флаги: Описание: cpse Rd, Rr Если Rd = Rr, to PC «— PC + 2(3), иначе PC «— PC + 1 0 <d <31; 0 <r<31 Нет Содержимое регистров Rd и Rr сравнивается с помощью вычитания, и в том случае, если Rd=Rr, следующая коман- да пропускается. Регистры и флаги остаются неизменными Тактовых циклов: 1 — если Rd ф Rr; 2 — если Rd = Rr и далее следует команда из одного слова; 3 — если Rd = Rr и далее следует команда из двух слов DEC Название: Decrement Синтаксис: Операция: Операнды: Влияние на флаги: Описание: dec Rd Rd «— Rd - 1 0 < d < 31 S, V, N, Z Из содержимого регистра Rd вычитается I. Флаг переноса не изменяется, благодаря чему Rd можно использовать в качестве счетчика цикла при выполнении арифметических операций над несколькими байтами. При декрементирова- нии беззнаковых операндов можно использовать только сравнения на равенство и неравенство Тактовых циклов: 1
Алфавитный перечень команд 241 ELPM Название: Extended Load Program Memory Синтаксис: a) elpm 6) elpm Rd, Z в) elpm Rd, Z+ Операция: a) RO <- (RAMPZ:Z) 6) Rd (RAMPZ:Z) в) Rd (RAMPZiZ); Z <- Z + 1 Операнды: 0 < d < 31 Влияние на флаги: Нет Описание: Байт памяти программ, адресуемый с помощью регистро- вой пары RAMPZ:Z, загружается в регистр R0 (вариант а) или Rd (вариант б и в). Разряд 0 указателя Z определяет, какой байт должен быть загружен: младший (разряд 0 = 0) или старший (разряд 0=1). Доступно адресное простран- ство более 64К Тактовых циклов: 3 EOR Название: Exclusive OR Синтаксис: eor Rd, Rr Операция: Rd <— Rd © Rr Операнды: 0 <d <31; 0 <r<31 Влияние на флаги: S, V=0, N, Z Описание: Объединение логической операцией “Исключающее ИЛИ” содержимого регистров Rd и Rr Тактовых циклов: 1 FMUL Название: Fractional Multiply Unsigned Синтаксис: fmul Rd, Rr Операция: Rl:R0 <— Rd x Rr (unsigned (1.15) <— unsigned(l .7) x un- signed(1.7) Операнды: 16 <d <23; 16 < r < 23 Влияние на флаги: C, Z Описание: Беззнаковое перемножение множителя Rr и множимого Rd. Результат записывается в регистровую пару Rl:R0 (R1 — старший байт; R0 — младший байт), после чего сдвигается на один разряд влево Тактовых циклов: 2 FMULS Название: Fractional Multiply Signed Синтаксис: fmuls Rd, Rr Операция: R1 :R0 Rd x Rr (signed (1.15)^- signed(l .7) x signed(1.7) Операнды: 16 <d <23; 16<r<23 16-6-767
242 Приложение В. Система команд микроконтроллеров AVR Влияние на флаги: С, Z Описание: Знаковое перемножение множителя Rr и множимого Rd. Результат записывается в регистровую пару R1:RO (R1 — старший байт; R0 — младший байт), после чего сдвигается на один разряд влево Тактовых циклов: 2 FMULSU Название: Fractional Multiply Signed with Unsigned Синтаксис: Операция: fmulsu Rd, Rr R1:RO «— Rd x Rr (signed (1.15) <— signed(1.7) x un- signed(l .7) Операнды: 16<d<23; 16<r<23 Влияние на флаги: Описание: C,Z Знаковое перемножение беззнакового множителя Rr и зна- кового множимого Rd. Результат записывается в регистро- вую пару R1:RO (R1 — старший байт; R0 — младший байт), после чего сдвигается на один разряд влево Тактовых циклов: 2 ICALL Название: Indirect Call to a Subroutine Синтаксис: Операция: Операнды: Влияние на флаги: Описание: icall PC^Z Нет Нет Косвенный вызов подпрограммы, на которую указывает указатель Z (гЗ 1 :гЗО). Адрес возврата (команда, следующая после команды icall), сохраняется в стеке Тактовых циклов: 3 IJMP Название: Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Indirect Jump ijmp PC <—Z Нет Нет Косвенный переход по адресу, на который указывает ука- затель Z (гЗ 1 :гЗО) Тактовых циклов: 2 IN Название: Load an I/O Location to Register Синтаксис: Операция: Операнды: in Rd, P Rd <—P 0 <d <31; 0<P<63
Алфавитный перечень команд 243 Влияние на флаги: Нет Описание: Тактовых циклов: Загрузка в регистр Rd содержимого порта ввода/вывода Р 1 INC Название: Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Increment inc Rd Rd <- Rd + 1 0 < d < 31 S, V, N, Z К содержимому регистра Rd прибавляется 1. Флаг перено- са не изменяется, благодаря чему Rd можно использовать в качестве счетчика цикла при выполнении арифметических операций над несколькими байтами. При инкрементирова- нии беззнаковых операндов можно использовать только сравнения на равенство и неравенство Тактовых циклов: 1 JMP Название: Jump Синтаксис: Операция: Операнды: Влияние на флаги: Описание: jmp k!6 (k22) PC^kl6(k22) 0 < k < 64K (0 < k < 4M) Нет Переход по абсолютному адресу kl6 (при 16-тиразрядной шине адреса) или к22 (при 22-хразрядной шине адреса) Тактовых циклов: 3 LD (LDD) Косвенная загрузка из SRAM в регистр с помощью указателя X Синтаксис: a) Id Rd, X (указатель X остается неизменным); б) Id Rd, Х+ (после передачи указатель X инкременти- руется); в) Id Rd, -X (перед передачей указатель X декременти- руется) Операция: a) Rd (X); б) Rd «—(X) / X «—X + 1; в) X X - 1 / Rd (X) Операнды: 0 < d < 31 Влияние на флаги: Описание: Нет Байт из ячейки SRAM, адресуемой с помощью X (г27:г26), загружается в Rd. В случае (а) после выполнения операции содержимое X остается неизменным, в случае (б) оно уве- личивается на 1, а в случае (в) — уменьшается на 1 еще до выполнения передачи Тактовых циклов: 2 16*
244 Приложение В. Система команд микроконтроллеров AVR Косвенная загрузка из SRAM в регистр с помощью указателя Y Синтаксис: a) Id Rd, Y (указатель Y остается неизменным); б) Id Rd, Y+ (после передачи указатель Y инкременти- руется); в) Id Rd, -Y (перед передачей указатель Y декременти- руется); г) idd Rd, Y+q (указатель Y остается неизменным; q — смещение) Операция: a) Rd <- (Y); б) Rd «—(Y) / Y «— Y + 1; в) Y <— Y - 1 / Rd <— (Y); г) Rd <- (Y+q) Операнды: 0<d <31; 0 < q < 63 Влияние на флаги: Описание: Нет Байт из ячейки SRAM, адресуемой с помощью указателя Y (г29:г28), загружается в регистр Rd. В случае (а) после вы- полнения операции содержимое Y остается неизменным, в случае (б) оно увеличивается на 1, а в случае (в) — умень- шается на 1 еще до выполнения передачи. В случае (г) со- держимое Y не изменяется, но во время выполнения опе- рации складывается с q Тактовых циклов: 2 Косвенная загрузка из SRAM в регистр с помощью указателя Z Синтаксис: a) Id Rd, Z (указатель Z остается неизменным); б) Id Rd, Z+ (после передачи указатель Z инкрементиру- ется); в) Id Rd, -Z (перед передачей указатель Z декременти- руется); г) Idd Rd, Z+q (указатель Z остается неизменным; q — смещение) Операция: a) Rd <- (Z); б) Rd (Z) / Z «—Z + 1; в) Z Z - 1 / Rd <- (Z); г) Rd (Z+q) Операнды: 0 <d <31; 0 < q < 63 Влияние на флаги: Описание: Нет Байт из ячейки SRAM, адресуемой с помощью указателя Z (гЗ 1 :гЗО), загружается в регистр Rd. В случае (а) после вы- полнения операции содержимое Z остается неизменным, в случае (б) оно увеличивается на 1, а в случае (в) — уменьшается на 1 еще до выполнения передачи. В случае (г) содержимое Z не изменяется, но во время выполнения операции складывается с q Тактовых циклов: 2
Алфавитный перечень команд 245 LDI Название: Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: LDS Название: Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: LPM Название: Синтаксис: Load Immediate Idi Rd, K8 Rd <- К.8 16 <d <31; 0<K8<255 Нет Загрузка в регистр Rd 8-битной константы К8 1 Load Direct from SRAM Ids Rd, kl6 Rd <—(k!6) 0 <d<31; 0 <kl6 < 65535 Нет Байт, извлеченный по абсолютному адресу к 16, загружает- ся непосредственно в регистр Rd 3 Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: LSL Название: Синтаксис: Операция: Load Program Memory a) 1pm 6) 1pm Rd, Z в) 1pm Rd, Z + a) RO <- (Z) 6) Rd <- (Z) в) Rd <- (Z); Z Z + 1 Нет Нет Байт памяти программ, адресуемый с помощью содержи- мого указателя Z (гЗ 1 :гЗО), загружается в регистр R0 (ва- риант а) или Rd (варианты б и в). Разряды 15... 1 указателя Z адресуют 16-разрядную ячейку программной флэш- памяти. Разряд 0 указателя Z определяет, какой байт дол- жен быть загружен: младший (разряд 0 = 0) или старший (разряд 0=1) 3 Logical Shift Left Isl Rd b7 b6 b5 b4 b3 b2 Ы bO Операнды: 0 < d < 31
246 Приложение В. Система команд микроконтроллеров AVR Влияние на флаги: Описание: Тактовых циклов: LSR Название: Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: МОУ Название: Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: Moyw Название: Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: MUL Название: Синтаксис: Операция: Операнды: Н, S, V, N, Z, С Все разряды регистра Rd сдвигаются на одну позицию вле- во; флагу переноса присваивается значение разряда 7, а в разряд 0 записывается лог. О 1 Logical Shift Right Isr Rd 0 <d <31 S, V, N=0, Z, C Все разряды регистра Rd сдвигаются на одну позицию вправо; флагу переноса присваивается значение разряда О, а в разряд 7 записывается лог. 0. Флаг переноса может ис- пользоваться для округления 1 Move Between Registers mov Rd, Rr Rd <—Rr 0 < d < 31; 0 < r < 31 Нет Копирование содержимого регистра Rr в регистр Rd. Со- держимое регистра Rr остается неизменным 1 Copy Register Word movw Rd+l:Rd, Rr+l:Rr Rd+l:Rd <—Rr+l:Rr 0 < d < 30; 0<r<30 Нет Копирование регистровой пары Rr+l:Rr в регистровую па- ру Rd+l:Rd. Содержимое регистровой пары Rr+l:Rr оста- ется неизменным 1 Multiply Unsigned mul Rd, Rr R1 :R0 «— Rd x Rr (unsigned <— unsigned x unsigned) 0 <d<31; 0<r<31
Алфавитный перечень команд 247 Влияние на флаги: С Описание: Беззнаковое перемножение множителя Rr и множимого Rd. Результат записывается в регистровую пару R1 :R0 (R1 — старший байт; R0 — младший байт). Разряд 15 ре- зультата копируется в флаг переноса Тактовых циклов: 2 MULS Название: Multiply Signed Синтаксис: Операция: Операнды: Влияние на флаги: Описание: muls Rd, Rr R1 :R0 «— Rd x Rr (signed <— signed x signed) 16<d<31; 16 <r<31 C Знаковое перемножение множителя Rr и множимого Rd. Результат записывается в регистровую пару R1:RO (R1 — старший байт; R0 — младший байт). Разряд 15 результата копируется в флаг переноса Тактовых циклов: 2 MULSU Название: Multiply Signed with Unsigned Синтаксис: Операция: Операнды: Влияние на флаги: Описание: mulsu Rd, Rr R1:RO <— Rd x Rr (signed <— signed x unsigned) 16<d<23; 16<r<23 C Знаковое перемножение беззнакового множителя Rr и зна- кового множимого Rd. Результат записывается в регистро- вую пару R1:RO (R1— старший байт; R0 — младший байт). Разряд 15 результата копируется в флаг переноса Тактовых циклов: 2 NEG Название: Two’s Complement Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: neg Rd Rd <- 0x00 - Rd 0 < d < 31 H, S, V, N, Z, C Реализация дополнения до двух содержимого регистра Rd 1 NOP Название: No Operation Синтаксис: Операция: Операнды: Влияние на флаги: пор Нет Нет Нет
248 Приложение В. Система команд микроконтроллеров AVR Описание: Эта команда задерживает на один такт ход программы Тактовых циклов: 1 OR Название: Logical OR Синтаксис: Операция: Операнды: Влияние на флаги: Описание: or Rd, Rr Rd «— Rd v Rr 0 <d<31; 0 <r <31 S, V=0, N, Z Объединение логической операцией “ИЛИ” содержимого регистров Rd и Rr. Результат записывается в регистр Rd Тактовых циклов: 1 ORI Название: Logical OR with Immediate Синтаксис: Операция: Операнды: Влияние на флаги: Описание: ori Rd, K8 Rd <— Rd v K8 16 <d <31; 0<K8<255 S, V=0, N, Z Объединение логической операцией “ИЛИ” содержимого регистра Rd и 8-битной константы К8. Результат записы- вается в регистр Rd Тактовых циклов: 1 OUT Название: Store Register to I/O Location Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: out P, Rr P <- Rr 0< r<31; 0<P<63 Нет Передача в порт Р содержимого регистра Rr 1 POP Название: Pop Register from Stack Синтаксис: Операция: Операнды: Влияние на флаги: Описание: pop Rd Rd <— Стек 0 < d < 31 Нет Указатель стека увеличивается на 1, после чего байт в памяти SRAM, на который указывает этот указатель, за- писывается в регистр Rd Тактовых циклов: 2
Алфавитный перечень команд 249 PUSH Название: Push Register to Stack Синтаксис: push Rd Операция: Стек <— Rd Операнды: 0 < г < 31 Влияние на флаги: Нет Описание: Содержимое регистра Rd сохраняется в ячейке памяти SRAM, адресуемой с помощью указатель стека, после чего этот указатель уменьшается на 1 Тактовых циклов: 2 RCALL Название: Relative Call to a Subroutine Синтаксис: rcall kl2 Операция: PC<—PC + kl2+l Операнды: -2K<kl2<2K Влияние на флаги: Нет Описание: Вызов подпрограммы, начало которой должно находиться в диапазоне PC ± 2К слов. Адрес возврата (команда, сле- дующая после команды rcall), помещается в стек Тактовых циклов: 3 RET Название: Return from Subroutine Синтаксис: ret Операция: PC Стек Операнды: Нет Влияние на флаги: Нет Описание: Выход из подпрограммы. Указатель стека увеличивается на 1, после чего соответствующий байт из SRAM копиру- ется в старший байт счетчика команд. Затем указатель сте- ка еще раз увеличивается на 1 и соответствующий байт из SRAM копируется в младший байт счетчика команд Тактовых циклов: 4 RETI Название: Return from Interrupt Синтаксис: reti Операция: PC <— Стек Операнды: Нет Влияние на флаги: 1=1 Описание: Возврат из подпрограммы обработки прерывания. Указа- тель стека увеличивается на 1, после чего байт из памяти SRAM, на который указывает этот указатель, копируется в старший байт счетчика команд. Затем указатель стека еще раз увеличивается на 1 и соответствующий байт из SRAM копируется в младший байт счетчика команд. Ус- танавливается флаг общего разрешения прерываний Тактовых циклов: 4
250 Приложение В. Система команд микроконтроллеров AVR RJMP Название: Relative Jump Синтаксис: Операция: Операнды: Влияние на флаги: Описание: rjmp kl2 PC<—PC + kl2 + 1 -2K<kl2<2K Нет Относительный переход по адресу, который должен нахо- диться в диапазоне PC ± 2К слов. Адрес возврата (коман- да, следующая после команды rjmp), помещается в стек Тактовых циклов: 2 ROL Название: Rotate Left trough Carry Синтаксис: Операция: rol Rd С *— Ь7 Ь6 Ь5 Ь4 ЬЗ Ь2 Ы bO Операнды: 0 < d < 31 Влияние на флаги: Описание: H, S, V, N, Z, C Все разряды регистра Rd сдвигаются на одну позицию вле- во; флаг С копируется в разряд 0, а разряд 7 — в С Тактовых циклов: 1 ROR Название: Rotate Right trough Carry Синтаксис: Операция: ror Rd Ь7 Ь6 Ь5 Ь4 ЬЗ Ь2 Ы bO >| C Операнды: 0 < d < 31 Влияние на флаги: Описание: S, V, N, Z, C Все разряды регистра Rd сдвигаются на одну позицию вправо; С копируется в разряд 7, а разряд 0 — в С Тактовых циклов: 1 SBC Название: Subtract with Carry Синтаксис: Операция: Операнды: Влияние на флаги: Описание: sbe Rd, Rr Rd <— Rd-Rr-C 0 < d < 31; 0 < r < 31 H, S, V, N, Z, C Содержимое регистра Rr и флаг переноса вычитаются из содержимого регистра Rd Тактовых циклов: 1
Алфавитный перечень команд 251 SBCI Название: Subtract Immediate with Carry Синтаксис: Операция: Операнды: Влияние на флаги: Описание: sbci Rd, K8 Rd Rd - K8 - C 16 < d < 31; 0 < K. < 255 H, S, V, N, Z, C 8-разрядная константа K8 и флаг переноса вычитаются из содержимого регистра Rd Тактовых циклов: 1 SBI Название: Set Bit in I/O-Register Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: sbi P, b I/O(P, b) +- 1 0<P<31;0<b<7 Нет Запись лог. 1 в разряд b регистра ввода/вывода Р 2 SBIC Название: Skip if Bit in I/O-Register is Cleared Синтаксис: Операция: Операнды: Влияние на флаги: Описание: sbic P, b Если I/O(b) = 0, to PC <— PC + 2 (3), иначе PC <— PC + 1 0<P<31;0<b<7 Нет Если разряд b регистра ввода/вывода Р содержит лог. 0, то следующая команда пропускается Тактовых циклов: 1 — если разряд b содержит лог. 1; 2 — если разряд b содержит лог. 0, и далее следует коман- да длиной в одно слово; 3 — если разряд b содержит лог. 0, и далее следует коман- да длиной в два слова (например, команда Ids) SBIS Название: Skip if Bit in I/O-Register is Set Синтаксис: Операция: Операнды: Влияние на флаги: Описание: sbis P, b Если I/O(b) = 1, to PC <— PC + 2 (3), иначе PC <— PC + 1 0 < P < 31; 0 < b < 7 Нет Если разряд b регистра ввода/вывода Р содержит лог. 1, то следующая команда пропускается Тактовых циклов: 1 — если разряд b содержит лог. 0; 2 — если разряд b содержит лог. 1, и далее следует коман- да длиной в одно слово; 3 — если разряд b содержит лог. 1, и далее следует коман- да длиной в два слова (например, команда sts)
252 Приложение В. Система команд микроконтроллеров AVR SBIW Название: Subtract Immediate from Word Синтаксис: Операция: Операнды: Влияние на флаги: Описание: sbiw Rdl, K6 Rdh:Rdl «- Rdh:Rdl-K6 dl g {24, 26, 28, 30}; 0 < K6 < 63 S, V, N, Z, C 6-разрядная константа Кб вычитается из содержимого ре- гистровой пары Rdh:Rdl Тактовых циклов: 2 SBR Название: Set Bit(s) in Register Синтаксис: Операция: Операнды: Влияние на флаги: Описание: sbr Rd, K8 Rd Rd v K8 16 < d < 31; 0 < K8 < 255 S, V = 0, N, Z Установка в регистре Rd разрядов, заданных с помощью операции “ИЛИ” с константой К8 Тактовых циклов: 1 SBRC Название: Skip if Bit in Register is Cleared Синтаксис: Операция: Операнды: Влияние на флаги: Описание: sbrc Rr, b Если Rr(b) = 0, to PC <— PC + 2 (3), иначе PC «— PC + 1 0<r<31;0<b<7 Нет Если разряд b регистра Rr содержит лог. 0, то следующая команда пропускается Тактовых циклов: 1 — если разряд b содержит лог. 1; 2 — если разряд b содержит лог. 0, и далее следует коман- да длиной в одно слово; 3 — если разряд b содержит лог. 0, и далее следует коман- да длиной в два слова (например, команда Ids) SBRS Название: Skip if Bit in Register is Set Синтаксис: Операция: Операнды: Влияние на флаги: Описание: sbrs Rr, b Если Rr(b) = 1, to PC <— PC + 2 (3), иначе PC «— PC + 1 0 < r < 31; 0<b<7 Нет Если разряд b регистра Rr содержит лог. 1, то следующая команда пропускается Тактовых циклов: 1 — если разряд b содержит лог. 0; 2 — если разряд b содержит лог. 1, и далее следует коман- да длиной в одно слово; 3 — если разряд b содержит лог. 1, и далее следует коман- да длиной в два слова (например, команда st s)
Алфавитный перечень команд 253 SEC Название: Set Carry Flag Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: sec C^- 1 Нет C = 1 Установка флага переноса в регистре SREG 1 SEH Название: Set Half Carry Flag Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: seh H— 1 Нет H = 1 Установка флага половинного переноса в регистре SREG 1 SEI Название: Set Global Interrupt Flag Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: sei I 1 Нет 1- 1 Установка флага общего разрешения прерываний в SREG 1 SEN Название: Set Negative Flag Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: sen N <- 1 Нет N= 1 Установка флага отрицательного результата в SREG 1 SER Название: Set all Bits in Register Синтаксис: Операция: Операнды: Влияние на флаги: Описание: ser Rd Rd <— OxFF 16 < d < 31 Нет Установка всех разрядов регистра посредством записи не- посредственного значения OxFF Тактовых циклов: 1
254 Приложение В. Система команд микроконтроллеров AVR SES Название: Set Signed Flag Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: ses S^- 1 Нет S = 1 Установка флага знака в регистре SREG 1 SET Название: Set Transfer Flag Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: set T^- 1 Нет Т= 1 Установка флага копирования в регистре SREG 1 SEV Название: Set Overflow Flag Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: sev V^- 1 Нет V= 1 Установка флага переполнения в регистре SREG 1 SEZ Название: Set Zero Flag Синтаксис: Операция: Операнды: Влияние на флаги: Описание: Тактовых циклов: sez Z<- 1 Нет Z= 1 Установка нулевого флага в регистре SREG 1 SLEEP Синтаксис: sleep Операция: Операнды: Влияние на флаги: Описание: Переводит процессор в “спящий” режим Нет Нет Процессор переходит в “спящий” режим, определенный с помощью регистра MCUCR Тактовых циклов: 1 (в некоторых случаях компания Atmel определяет для команды sleep три тактовых цикла).
Алфавитный перечень команд 255 SPM Название: Store Program Memory Синтаксис: Операция: spm a) (RAMPZ:Z)«- OxFFFF 6) (RAMPZ.Z) *—R1.R0 в) (RAMPZ:Z)«- TEMP Операнды: Нет Влияние на флаги: Описание: Нет Эта команда может быть использована для стирания неко- торой страницы в памяти программ, а также для записи в память программ слова или страницы Тактовых циклов: Зависит от операции ST (STD) Косвенное сохранение содержимого регистра в SRAM с помощью указателя X Синтаксис: a) st X, Rr (указатель X остается неизменным); б) st Х+, Rr (после передачи указатель X инкременти- руется); в) st -X, Rr (перед передачей указатель X декременти- руется) Операция: а) (X) Rr; б) (X) <—Rr/X <—X + 1; в) X X - 1 / (X) <- Rd Операнды: 0 < г < 31 Влияние на флаги: Описание: Нет Содержимое регистра Rr сохраняется в ячейку SRAM, ад- ресуемую с помощью указателя X (г27:г26). В случае (а) после выполнения операции содержимое X остается неиз- менным, в случае (б) оно увеличивается на 1, а в случае (в) — уменьшается на 1 еще до выполнения передачи Тактовых циклов: 2 Косвенное сохранение содержимого регистра в SRAM с помощью указателя Y Синтаксис: a) st Y, Rr (указатель Y остается неизменным); б) st Y+, Rr (после передачи указатель Y инкременти- руется); в) st -Y, Rr (перед передачей указатель Y декременти- руется); г) std Y+q, Rr (указатель Y остается неизменным; q — смещение); Операция: a) (Y) Rr; б) (Y) <—Rr/Y«—Y+ 1; в) Y <— Y - 1 / (Y) <— Rr; г) (Y+q) <— Rr
256 Приложение В. Система команд микроконтроллеров AVR Операнды: 0 < г<31; 0 < q < 63 Влияние на флаги: Описание: Нет Содержимое регистра Rr сохраняется в ячейку SRAM, адре- суемую с помощью указателя Y (г29:г28). В случае (а) после выполнения операции содержимое Y остается неизменным, в случае (б) оно увеличивается на 1, а в случае (в) — умень- шается на 1 еще до выполнения передачи. В случае (г) со- держимое Y не изменяется, но во время выполнения опера- ции складывается с q Тактовых циклов: 2 Косвенное сохранение содержимого регистра в SRAM с помощью указателя Z Синтаксис: a) st Z, Rr (указатель Z остается неизменным); б) st Z+, Rr (после передачи указатель Z инкрементиру- ется); в) st — Z, Rr (перед передачей указатель Z декременти- руется); г) std Z+q, Rr (указатель Z остается неизменным; q — смещение); Операция: a) (Z)«- Rr; б) (Z) <—Rr / ZZ + 1; в) Z <- Z - 1 / (Z) +- Rr; г) (Z+q) «— Rr Операнды: 0< r<31; 0 < q < 63 Влияние на флаги: Описание: Нет Содержимое регистра Rr сохраняется в ячейку SRAM, адре- суемую с помощью указателя Z (гЗ 1 :гЗО). В случае (а) после выполнения операции содержимое Z остается неизменным, в случае (б) оно увеличивается на 1, а в случае (в) — умень- шается на 1 еще до выполнения передачи. В случае (г) со- держимое Z не изменяется, но во время выполнения опера- ции складывается с q Тактовых циклов: 2 STS Название: Store Direct to SRAM Синтаксис: Операция: Операнды: Влияние на флаги: Описание: sts kl6, Rr (kl6)<— Rr 0 < r< 31; 0< k!6 < 65535 Нет Загрузка в ячейку SRAM по абсолютному 16-битному ад- ресу kl6 содержимого регистра Rr Тактовых циклов: 3
Алфавитный перечень команд 257 SUB Название: Subtract without Carry Синтаксис: Операция: Операнды: Влияние на флаги: Описание: sub Rd, Rr Rd«— Rd - Rr 0 <d<31; 0<r<31 H, S, V, N, Z, C Содержимое регистра Rr вычитается из содержимого реги- стра Rd Тактовых циклов: 1 SUBI Название: Subtract Immediate without Carry Синтаксис: Операция: Операнды: Влияние на флаги: Описание: subi Rd, K8 Rd «— Rd - K8 16 < d < 31; 0 < K8 < 255 H, S, V, N, Z, C 8-разрядная константа K8 вычитается из содержимого ре- гистра Rd Тактовых циклов: 1 SWAP Название: Swap Nibbles Синтаксис: Операция: Операнды: Влияние на флаги: Описание: swap Rd Rd(7-4) <- Rd(3-0), Rd(3-0) <- Rd(7-4) 0 < d < 31 Нет Старший и младший полубайты регистра Rd меняются местами Тактовых циклов: 1 Название: Test for Zero or Minus Синтаксис: Операция: Операнды: Влияние на флаги: Описание: tst Rd Rd <— Rd a Rd 0 < d < 31 S, V = 0, N, Z Проверка, является ли содержимое регистра Rd нулевым или отрицательным, с помощью применения логической операции “И”. Само содержимое регистра Rd остается не- изменным Тактовых циклов: 1 WDR Название: Watchdog Reset Синтаксис: Операция: wdr Перевод сторожевого таймера в исходное состояние 17-6-767
258 Приложение В. Система команд микроконтроллеров AVR Операнды: Нет Влияние на флаги: Нет Описание: Устанавливает в исходное состояние сторожевой таймер Тактовых циклов: 1 Команды по категориям Арифметические и логические команды: adc Rd, Rr — содержимое регистра Rr и флаг переноса прибавляются к со- держимому регистра Rd; add Rd, Rr — содержимое регистра Rr прибавляется к содержимому реги- стра Rd; adiw Rdl, Кб — константа Кб прибавляется к содержимому регистровой пары Rdh:Rdl; and Rd, Rr — объединение логической операцией “И” содержимого реги- стров Rd и Rr; andi Rd, К8 — объединение логической операцией “И” содержимого реги- стра Rd и восьмиразрядной константы К8; cbr Rd, К8 — сброс в регистре Rd разрядов, заданных с помощью К8; clr Rd — очистка регистра Rd; com Rd — реализация дополнения до единицы содержимого регистра Rd; ср Rd, Rr — сравнение содержимого регистров Rd и Rr (содержимое обоих регистров не меняется); срс Rd, Rr — сравнение содержимого регистров Rd и Rr с учетом флага пе- реноса; cpi Rd, К8 — сравнение содержимого регистра Rd с восьмиразрядной кон- стантой К8; cpse Rd, Rr — содержимое регистров Rd и Rr сравнивается, и в том случае, если Rd=Rr, следующая команда пропускается; dec Rd — вычитание 1 из содержимого регистра Rd; eor Rd, Rr — объединение логической операцией “Исключающее ИЛИ” содержимого регистров Rd и Rr; fmul Rd, Rr — беззнаковое перемножение множителя Rr и множимого Rd; результат записывается в регистровую пару R1 :R0, после чего сдвигается на один разряд влево; fmuls Rd, Rr — знаковое перемножение множителя Rr и множимого Rd; результат записывается в регистровую пару R1:RO, после чего сдвигается на один разряд влево; fmulsu Rd, Rr — перемножение беззнакового множителя Rr и знакового множимого Rd; результат (со знаком) записывается в регистровую пару R1:RO, после чего сдвигается на один разряд влево; inc Rd — прибавление 1 к содержимому регистра Rd;
Команды по категориям 259 mul Rd, Rr — беззнаковое перемножение множителя Rr и множимого Rd; результат записывается в регистровую пару R1 :R0; разряд 15 результата копируется в флаг переноса; muls Rd, Rr — знаковое перемножение множителя Rr и множимого Rd; ре- зультат записывается в регистровую пару R1:RO; разряд 15 результата ко- пируется в флаг переноса; mulsu Rd, Rr — знаковое перемножение беззнакового множителя Rr и зна- кового множимого Rd; результат записывается в регистровую пару R1 :R0; разряд 15 результата копируется в флаг переноса; neg Rd — реализация дополнения до двух содержимого регистра Rd; or Rd, Rr — объединение логической операцией “ИЛИ” содержимого реги- стров Rd и Rr; ori Rd, К8 — объединение логической операцией “ИЛИ” содержимого ре- гистра Rd и восьмиразрядной константы К8; sbc Rd, Rr — содержимое регистра Rr и флаг переноса вычитаются из со- держимого регистра Rd; sbci Rd, К8 — восьмиразрядная константа К8 и флаг переноса вычитаются из содержимого регистра Rd; . sbiw Rdl, Кб — константа Кб вычитается из содержимого регистровой па- ры RdlrRdl; sbr Rd, К8 — установка в регистре Rd разрядов, заданных с помощью К8; ser Rd — загрузка в регистр Rd значения $FF; sub Rd, Rr — содержимое регистра Rr вычитается из содержимого регист- ра Rd; subi Rd, К8 — восьмиразрядная константа К8 вычитается из содержимого регистра Rd; tst Rd — проверка, является ли содержимое регистра Rd нулевым или от- рицательным. Команды пересылки: bld Rd, b — копирование флага Т в разряд b регистра Rd; bst Rd, b — копирование в флаг Т разряда b регистра Rd; elpm; elpm Rd, Z; elpm Rd, Z+ — байт памяти программ, адресуемый с по- мощью регистровой пары RAMPZ:Z, загружается в регистр R0 или Rd; in Rd, Р — загрузка в регистр Rd содержимого порта ввода/вывода Р; Id Rd, X; Id Rd, X+; Id Rd, -X — загрузка в регистр Rd содержимого ячейки SRAM, адресуемой с помощью указателя X; Id Rd, Y; Id Rd, Y+; Id Rd, -Y — загрузка в регистр Rd содержимого ячейки SRAM, адресуемой с помощью указателя Y; . Id Rd, Z; Id Rd, Z+; Id Rd, -Z — загрузка в регистр Rd содержимого ячейки SRAM, адресуемой с помощью указателя Z; . Idd Rd, Y+q — загрузка в регистр Rd содержимого ячейки SRAM, адресуе- мой с помощью суммы q и содержимого указателя Y; 17*
260 Приложение В. Система команд микроконтроллеров AVR Idd Rd, Z+q — загрузка в регистр Rd содержимого ячейки SRAM, адресуе- мой с помощью суммы q и содержимого указателя Z; Idi Rd, К8 — загрузка в регистр Rd восьмиразрядной константы К8; Ids Rd, kl6 — загрузка в регистр Rd содержимого ячейки SRAM по адресу к!6; 1pm; 1pm Rd, Z; 1pm Rd, Z+ — загрузка в регистр RO или Rd содержимого ячейки памяти Flash-EPROM, адресуемой с помощью указателя Z; mov Rd, Rr — копирование содержимого регистра Rr в регистр Rd; movw Rd+1 :Rd, Rr+1 :Rr — копирование регистровой пары Rr+1 :Rr в реги- стровую пару Rd+1 :Rd; out P, Rr — загрузка в порт ввода/вывода Р содержимого регистра Rr; pop Rd — загрузка в регистр Rd содержимого ячейки стека, адресуемой с помощью указателя стека; . push Rr — загрузка в ячейку стека, адресуемую с помощью указателя сте- ка, содержимого регистра Rr; spin — эта команда может быть использована для стирания некоторой страницы в памяти программ, а также для записи в память программ слова или страницы; st X, Rr; st Х+, Rr; st -X, Rr — загрузка в ячейку SRAM, адресуемую с по- мощью указателя X, содержимого регистра Rr; st Y, Rr; st Y+, Rr; st -Y, Rr — загрузка в ячейку SRAM, адресуемую с по- мощью указателя Y, содержимого регистра Rr; st Z, Rr; st Z+, Rr; st -Z, Rr — загрузка в ячейку SRAM, адресуемую с по- мощью указателя Z, содержимого регистра Rr; std Y+q, Rr — загрузка в ячейку SRAM, адресуемую с помощью суммы q и содержимого указателя Y, содержимого регистра Rr; std Z+q, Rr — загрузка в ячейку SRAM, адресуемую с помощью суммы q и содержимого указателя Z, содержимого регистра Rr; sts kl 6, Rr — загрузка в ячейку SRAM по адресу kl6 содержимого регистра Rr. Команды ветвления: brbc s, k7 — если разряд s регистра SREG содержит лог. О, то выполняется относительный переход по адресу в диапазоне от РС-64 до РС+63; brbs s, k7 — если разряд s регистра SREG содержит лог. 1, то выполняется относительный переход по адресу в диапазоне от РС-64 до РС+63; brcc к7 — если сброшен флаг переноса, то выполняется относительный переход по адресу в диапазоне от РС-64 до РС+63; brcs к7 — если установлен флаг переноса, то выполняется относительный переход по адресу в диапазоне от РС-64 до РС+63; breq к7 — если установлен нулевой флаг, то выполняется относительный переход по адресу в диапазоне от РС-64 до РС+63;
Команды по категориям 261 brge k7 — если сброшен флаг знака, то выполняется относительный пере- ход по адресу в диапазоне от РС-64 до РС+63; brhc k7 — если сброшен флаг половинного переноса, то выполняется от- носительный переход по адресу в диапазоне от РС-64 до РС+63; brhs k7 — если установлен флаг половинного переноса, то выполняется относительный переход по адресу в диапазоне от РС-64 до РС+63; brid k7 — если сброшен флаг общего разрешения прерываний, то выпол- няется относительный переход по адресу в диапазоне от РС-64 до РС+63; brie k7 — если установлен флаг общего разрешения прерываний, то вы- полняется относительный переход по адресу в диапазоне от РС-64 до РС+63; brio k7 — если установлен флаг переноса, то выполняется относительный переход по адресу в диапазоне от РС-64 до РС+63; brlt k7 — если установлен флаг знака, то выполняется относительный пе- реход по адресу в диапазоне от РС-64 до РС+63; brmi k7 — если установлен флаг отрицательного результата, то выполня- ется относительный переход по адресу в диапазоне от РС-64 до РС+63; brne k7 —- если сброшен нулевой флаг, то выполняется относительный пе- реход по адресу в диапазоне от РС-64 до РС+63; brpl k7 — если сброшен флаг отрицательного результата, то выполняется относительный переход по адресу в диапазоне от PC—64 до РС+63; brsh k7 — если сброшен флаг переноса, то выполняется относительный переход по адресу в диапазоне от РС-64 до РС+63; brtc k7 — если сброшен флаг копирования, то выполняется относительный переход по адресу в диапазоне от РС-64 до РС+63; brts k7 -— если установлен флаг копирования, то выполняется относитель- ный переход по адресу в диапазоне от РС-64 до РС+63; brvc k7 — если сброшен флаг переполнения, то выполняется относитель- ный переход по адресу в диапазоне от РС-64 до РС+63; brvs k7 — если установлен флаг переполнения, то выполняется относи- тельный переход по адресу в диапазоне от РС-64 до РС+63; call к — вызов подпрограммы по адресу к; cpse Rd, Rr — содержимое регистров Rd и Rr сравнивается, и в том случае, если Rd=Rr, следующая команда пропускается; icall — косвенный вызов подпрограммы, на которую указывает указатель Z; ijmp — косвенный переход по адресу, на который указывает указатель Z; jmp к — абсолютный переход по адресу к; rcall к — вызов подпрограммы в пределах диапазона от РС-2К до РС+2К; ret — возврат из подпрограммы, адрес извлекается из стека; reti — возврат из подпрограммы обработки прерывания по адресу, извле- ченному из стека; устанавливается флаг общего разрешения прерываний;
262 Приложение В. Система команд микроконтроллеров AVR rjmp к — относительный переход по адресу в пределах диапазона от РС- 2К до РС+2К (слов); sbic Р, b — если разряд b регистра ввода/вывода Р содержит лог. О, то сле- дующая команда пропускается; sbis Р, b — если разряд b регистра ввода/вывода Р содержит лог. 1, то сле- дующая команда пропускается; sbrc Rr, b — если разряд b регистра Rr содержит лог. О, то следующая ко- манда пропускается; sbrs Rr, b — если разряд b регистра Rr содержит лог. 1, то следующая ко- манда пропускается. Команды поразрядных операций: asr Rd — разряды 0...6 регистра Rd сдвигаются на одну позицию вправо; разряд 0 сохраняется как флаг переноса; разряд 7 не изменяется; bclr s — сброс разряда s в регистре SREG; bld Rd, b — копирование флага Т в разряд b регистра Rd; bset s — установка разряда s в регистре SREG; bst Rd, b — копирование в флаг Т разряда b регистра Rd; cbi Р, b — запись лог. 0 в разряд b регистра ввода/вывода Р; cbr Rd, К8 — сброс в регистре Rd разрядов, заданных с помощью К8; clc — сброс флага переноса в регистре SREG; clh — сброс флага половинного переноса в регистре SREG; cli — сброс флага общего разрешения прерываний в регистре SREG; cln — сброс флага отрицательного результата в регистре SREG; cis — сброс флага знака в регистре SREG; clt — сброс флага копирования в регистре SREG; civ — сброс флага переполнения в регистре SREG; clz — сброс нулевого флага в регистре SREG; Isl Rd — все разряды регистра Rd сдвигаются на одну позицию влево; фла- гу переноса присваивается значение разряда 7, а в разряд 0 записывается 0; Isr Rd — все разряды регистра Rd сдвигаются на одну позицию вправо; флагу переноса присваивается значение разряда 0, а в разряд 7 записывает- ся 0; rol Rd — все разряды регистра Rd сдвигаются на одну позицию влево; в разряд 0 записывается флаг переноса, после чего разряд 7 сохраняется как флаг переноса; ror Rd — все разряды регистра Rd сдвигаются на одну позицию вправо; в разряд 7 записывается флаг переноса, после чего разряд 0 сохраняется как флаг переноса; sbi Р, b — запись лог. 1 в разряд b регистра ввода/вывода Р; sbr Rd, К8 — установка в регистре Rd разрядов, заданных с помощью К8; sec — установка флага переноса в регистре SREG;
Команды по категориям 263 seh — установка флага половинного переноса в регистре SREG; sei — установка флага общего разрешения прерываний в регистре SREG; sen — установка флага отрицательного результата в регистре SREG; ses — установка флага знака в регистре SREG; set — установка флага копирования в регистре SREG; sev — установка флага переполнения в регистре SREG; sez — установка нулевого флага в регистре SREG; swap Rd — старший и младший полубайты регистра Rd меняются местами. Команды управления микроконтроллером: пор — нет операции; sleep — переход в “спящий” режим; wdr — сброс сторожевого таймера.
Приложение Г Система команд микроконтроллеров PIC Ниже, в описании машинных команд микроконтроллеров AVR будут исполь- зованы некоторые условные обозначения. Перечислим их: F — адрес регистра в регистровом файле; W — рабочий регистр (накапливающий сумматор); b — номер разряда в регистре; К — литеральное поле, константа или метка; А — адрес; а — указатель доступа в микроконтроллерах семейства PIC18: если а=1, то при доступе используется регистр выбора банка, иначе — банк доступа; Р — обозначение порта ввода-вывода; d — указатель выбора направления; s — указатель операции; PC — счетчик команд; SP — указатель вершины стека; Стек — область памяти для промежуточного хранения данных; С — флаг переноса (регистр STATUS); DC — флаг десятичного переноса (регистр STATUS); Z — флаг нулевого результата (регистр STATUS); N — флаг отрицательного результата (регистр STATUS); OV — флаг переполнения (регистр STATUS); PD — флаг режима пониженного энергопотребления (регистр STATUS); ТО — флаг подачи напряжения (регистр STATUS). Алфавитный перечень команд ADDLW Название: Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: Add Literal and W addlw К \V<- W + K C, DC, Z (OV, N) Сложение константы К с содержимым регистра W 1 Во всех микроконтроллерах PIC, кроме семейств с 12-ти- разрядным процессорным ядром
Алфавитный перечень команд 265 ADDWF Название: Add W and F Синтаксис: a) addwf F, d 6) addwf F, d, a Операция: Если (d = 1), to F <— F + W, иначе W <— F + W Влияние на флаги: Описание: C, DC, Z (N, OV) Содержимое регистра F складывается с содержимым регист- ра W. Результат размещается в соответствии со значением d Тактовых циклов: 1 Использование: Во всех семействах микроконтроллеров PIC (случай б — для семейства PIC18) ADDWFC Название: Add W and F with Carry Синтаксис: a) addwfc F, d 6) addwfc F, d, a Операция: Если (d = 1), to W «— F + W + С, иначе F «— F + W + C Влияние на флаги: Описание: C, DC, Z, OV (N) Содержимое регистра F складывается с содержимым реги- стра W и флагом переноса. Результат размещается в соот- ветствии со значением указателя d Тактовых циклов: 1 Использование: Только в микроконтроллерах семейств PIC17 (а) и PIC18 (б) ANDLW Название: AND Literal with W Синтаксис: Операция: Влияние на флаги: Описание: andlw К W&K z Поразрядное объединение операцией “И” константы К с содержимым регистра W Тактовых циклов: 1 Использование: Во всех семействах микроконтроллеров PIC ANDWF Название: AND W with F Синтаксис: a) andwf F, d 6) andwf F, d, a Операция: Если (d = 1), to F <— F & W, иначе W <— F & W Влияние на флаги: Описание: Z Поразрядное объединение операцией “И” содержимого ре- гистра F с содержимым регистра W. Результат размещается в соответствии со значением d Тактовых циклов: 1 Использование: Во всех семействах микроконтроллеров PIC (случай б — для семейства PIC 18)
266 Приложение Г. Система команд микроконтроллеров PIC ВС Название: Branch if Carry Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: be К Если С=1, то PC *— PC + 2 + К Нет Переход к метке К, если установлен флаг переноса 1(2) Только в микроконтроллерах семейства PIC 18 BCF Название: Bit Clear F Синтаксис: a)bcf F, b 6) bef F, b, a Операция: F(b) — 0 Влияние на флаги: Описание: Тактовых циклов: Использование: Нет Сброс в лог. 0 разряда b регистра F 1 Во всех семействах микроконтроллеров PIC (случай б — для семейства PIC 18) BN Название: Branch if Negative Синтаксис: Операция: Влияние на флаги: Описание: bn К Если N=l, то PC <— PC + 2 + К Нет Переход к метке К, если установлен флаг отрицательного результата Тактовых циклов: 1(2) Использование: Только в микроконтроллерах семейства PIC18 BNC Название: Branch if Not Carry Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: bnc К Если C=0, то PC PC + 2 + К Нет Переход к метке К, если флаг переноса не установлен 1(2) Только в микроконтроллерах семейства PIC18 BNN Название: Branch if Not Negative Синтаксис: Операция: Влияние на флаги: Описание: bnn К Если N=0, то PC «— PC + 2 + К Нет Переход к метке К, если флаг отрицательного сброшен
Алфавитный перечень команд 267 Тактовых циклов: 1(2) Использование: Только в микроконтроллерах семейства PIC18 BNOV Название: Branch if Not Overflow Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: bnov К Если OV=0, то PC <— PC + 2 + К Нет Переход к метке К, если сброшен флаг переполнения 1(2) Только в микроконтроллерах семейства PICI8 BNZ Название: Branch if Not Zero Синтаксис: Операция: Влияние на флаги: Описание: bnz К Если Z=0, то PC *— PC + 2 + К Нет Переход к метке К, если результат выполнения предыду- щей операции не равен 0 Тактовых циклов: 1(2) Использование: Только в микроконтроллерах семейства PIC18 BOV Название: Branch if Overflow Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: bov К Если OV=1, то PC «— PC + 2 + К Нет Переход к метке К, если установлен флаг переполнения 1(2) Только в микроконтроллерах семейства PIC 18 BRA Название: Branch Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: bra К PC *— PC + 2 + К Нет Безусловный переход к метке К 2 Только в микроконтроллерах семейства PIC 18 BSF Название: Bit Set F Синтаксис: a)bsf F, b 6)bsf F, b, a Операция: F(b) <- 1 Влияние на флаги: Нет
268 Приложение Г. Система команд микроконтроллеров PIC Описание: Установка в лог. 1 разряда b регистра F Тактовых циклов: Использование: 1 Во всех семействах микроконтроллеров PIC (случай б — для семейства PIC18) BTFSC Название: Bit Test F, Skip if Clear Синтаксис: a) btfsc F, b 6) btfsc F, b, a Операция: Если F(b)=O, to PC «— PC + 1 Влияние на флаги: Описание: Нет Пропуск следующей команды, если разряд b в регистре F равен 0 Тактовых циклов: 1(2) Использование: Во всех семействах микроконтроллеров PIC (случай б — для семейства PIC18) BTFSS Название: Bit Test F, Skip if Set Синтаксис: a)btfss F, b 6)btfss F, b, a Операция: Если F(b)=l, то PC «— PC + 1 Влияние на флаги: Описание: Нет Пропуск следующей команды, если разряд b в регистре F равен 1 Тактовых циклов: 1(2) Использование: Во всех семействах микроконтроллеров PIC (случай б — для семейства PIC 18) BTG Название: Bit Toggle F Синтаксис: a) btg F, b 6) btg F, b, a Операция: F«—FA(1 <<b) Влияние на флаги: Описание: Тактовых циклов: Использование: Нет Изменение состояния разряда b регистра F 1 Только в микроконтроллерах семейств PIC17 (а) и PIC18 (б) BZ Название: Branch if Zero Синтаксис: Операция: Влияние на флаги: Описание: bz К Если Z=l, то PC <- PC + 2 + К Нет Переход к метке К, если результат выполнения предыду- щей операции равен 0
Алфавитный перечень команд 269 Тактовых циклов: Использование: 1(2) Только в микроконтроллерах семейства PIC18 CALL Название: Call a Subroutine Синтаксис: a) call А б) call А в) call К г) call К, s Операция: a) [SP] <- PC; SP SP+1; PC = ((STATUS & ОхОЕО) « 4+А; б) [SP] <- PC; SP SP+1; PC - ((PCLATH « 8) & 0x01800) + А; в) [SP] <- PC; PCLATH PC(15:13) + K(12:8); PCL K(7:0); r) [SP] <- PC. Если s — 1, to [SP] <- W; [SP] <- STATUS; [SP]«—BSR. PC «—K. Влияние на флаги: Нет Описание: Переход к подпрограмме Тактовых циклов: 2 Использование: Во всех семействах микроконтроллеров PIC: а) — 12-ти - разрядное ядро; б) — 14-тиразряднео ядро; в) — семейст- во PIC 17; г) — семейство PIC 18 CLRF Название: Clear F Синтаксис: a) clrf F б) clrf F, s в) clrf F, а Операция: a) F <- 0 б) F «— 0. Если s = 0, то W <— 0 в) F«—0 Влияние на флаги: Z<- 1 Описание: Стирание регистра F Тактовых циклов: 1 Использование: Во всех семействах микроконтроллеров PIC: а) — 12-ти- и 14-тиразрядное ядро; б) — семейство PIC17; в) — семей- ство PIC 18 CLRW Название: Clear W Синтаксис: clrw Операция: W^-0 Влияние на флаги: Z^- 1 Описание: Стирание регистра W Тактовых циклов: 1 Использование: В микроконтроллерах с 12-ти- и 14-тиразрядным ядром
270 Приложение Г. Система команд микроконтроллеров PIC CLRWDT Название: Clear Watchdog Timer Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: clrwdt WDT«—0 TO*— 1;PD«— 1 Сброс сторожевого таймера 1 Во всех семействах микроконтроллеров PIC COMF Название: Complement F Синтаксис: a) comf F, d б) comf F, d, a Операция: Если d=l, то F *— F Л OxOFF, иначе W «— F л OxOFF Влияние на флаги: Описание: Z(N) Инвертирование содержимого регистра F и размещение ре- зультата в соответствии со значением указателя d Тактовых циклов: 1 Использование: Во всех семействах микроконтроллеров PIC (случай б — для семейства PIC18) CPFSEQ Название: Compare F with W, Skip if Equal Синтаксис: a) cpfseq F 6) cpfseq F, a Операция: Если (F - W) = 0, to PC *— PC + 1 Влияние на флаги: Описание: Нет Сравнение содержимого регистров F и W. В случае равен- ства следующая команда пропускается Тактовых циклов: 1, 2 или 3 Использование: Только в микроконтроллерах семейств PIC17 (а) и PIC18 (б) CPFSGT Название: Compare F with W, Skip if Greater Синтаксис: a) cpfsgt F 6) cpfsgt F, a Операция: Если (F - W) > 0, to PC *— PC + 1 Влияние на флаги: Описание: Нет Сравнение содержимого регистров F и W. Если F больше W, то следующая команда пропускается Тактовых циклов: 1, 2 или 3 Использование: Только в микроконтроллерах семейств PIC17 (а) и PIC18 (б)
Алфавитный перечень команд 271 CPFSLT Название: Compare F with W, Skip if Less Синтаксис: a) cpfslt F 6) cpfslt F, a Операция: Если (F - W) < 0, to PC «— PC + 1 Влияние на флаги: Описание: Нет Сравнение содержимого регистров F и W. Если F меньше W, то следующая команда пропускается Тактовых циклов: 1, 2 или 3 Использование: Только в микроконтроллерах семейств PIC17 (а) и PIC18 (б) DAW Название: Decimal Amendment of W Синтаксис: a) daw F, s 6) daw Операция: а) Если (W&OxOF) > 9, то при s=0 W (W & OxOF) + 0x010, а при s=l F <— (W & OxOF) + 0x010; б) Если (W&OxOF) > 9, то при s=0 W (W & OxOF) + 0x010 Влияние на флаги: Нет Описание: Тактовых циклов: Использование: Десятичная коррекция содержимого регистра W 1 Только в микроконтроллерах PIC17 (а) и PIC18 (б) DCFSNZ Название: Decrement F, Skip if Not Zero Синтаксис: a) dcfsnz F, d 6) dcfsnz F, d, a Операция: Если d=l, to F <— F- 1, иначе W <— F - 1. Если (F-l) 0, to PC «— PC + 1 Влияние на флаги: Нет Описание: Декрементирование содержимого регистра F и размещение результата в соответствии со значением указателя d. Если результат не равен 0, то следующая команда пропускается Тактовых циклов: 1, 2 или 3 Использование: Только в микроконтроллерах семейств PIC 17 (а) и PIC 18 (б) DECF Название: Decrement F Синтаксис: a) decf F, d б) decf F, d, a Операция: Если d=l, то F «— F - 1, иначе W «— F - 1 Влияние на флаги: Описание: Z (OV, DC, C, N) Декрементирование содержимого регистра F и размещение результата в соответствии со значением указателя d Тактовых циклов: 1 Использование: Во всех семействах микроконтроллеров PIC (случай б — для семейства PIC 18)
272 Приложение Г. Система команд микроконтроллеров PIC DECFSZ Название: Decrement F, Skip if Zero Синтаксис: a) decfsz F, d 6) decf sz F, d, a Операция: Если d= 1, то F«— F - 1, иначе W <— F — 1. Если (F-l) = 0, to PC <- PC + 1 Влияние на флаги: Нет Описание: Декрементирование содержимого регистра F и размещение результата в соответствии со значением указателя d. Если результат равен 0, то следующая команда пропускается Тактовых циклов: 1 (2) Использование: Во всех семействах микроконтроллеров PIC (случай б — для семейства PIC18) GOTO Название: Go То Address Синтаксис: a) goto А б) goto А в) goto К г) goto К Операция: a) PC ((STATUS & ОхОЕО) « 4) + А; б) PC <- ((PCLATH « 8) & 0x01800) + А; в) PCLATH <- РС(15:13) + К(12:8); PCL = К(7:0); г) PC «— К Влияние на флаги: Нет Описание: Тактовых циклов: Использование: Безусловный переход 2 Во всех семействах микроконтроллеров PIC: а) — 12-ти- разрядное ядро; б) — 14-тиразряднео ядро; в) — семейст- во PIC 17; г) — семейство PIC 18 INCF Название: Increment F Синтаксис: a) incf F, d б) incf F, d, a Операция: Если d=l, то F «— F + 1, иначе W «— F + 1 Влияние на флаги: Описание: Z (OV, DC, C, N) Инкрементирование содержимого регистра F и размещение результата в соответствии со значением указателя d Тактовых циклов: 1 Использование: Во всех семействах микроконтроллеров PIC (случай б — для семейства PIC 18) INCFSZ Название: Increment F, Skip if Zero Синтаксис: a) incfsz F, d 6) incfsz F, d, a
Алфавитный перечень команд 273 Операция: Если d= 1, то F <— F + 1, иначе W «— F + 1. Если (F+1)= 0, то PC «— PC + 1 Влияние на флаги: Нет Описание: Инкрементирование содержимого регистра F и размещение результата в соответствии со значением указателя d. Если результат равен 0, то следующая команда пропускается Тактовых циклов: 1 (2) Использование: Во всех семействах микроконтроллеров PIC (случай б — для семейства PIC 18) INFSNZ Название: Increment F, Skip if Not Zero Синтаксис: a) infsnz F, d 6) infsnz F, d, a Операция: Если d=l, to F «— F + 1, иначе W «— F + 1. Если (F+1) # 0, to PC <- PC + 1 Влияние на флаги: Нет Описание: Инкрементирование содержимого регистра F и размещение результата в соответствии со значением указателя d. Если результат не равен 0, то следующая команда пропускается Тактовых циклов: 1, 2 или 3 Использование: Только в микроконтроллерах семейств PIC17 (а) и PIC18 (б) IORLW Название: Inclusive OR Literal with W Синтаксис: Операция: Влияние на флаги: Описание: iorlw К W^-W|K Z(N) Поразрядное объединение содержимого регистра W и кон- станты К логической операцией “ИЛИ” Тактовых циклов: 1 Использование: Только в микроконтроллерах семейств PIC 17 и PIC 18 IORWF Название: Inclusive OR W with F Синтаксис: a) iorwf F, d 6) iorwf F, d, a Операция: Если d=l, то F <— F | W, иначе W <— F | W Влияние на флаги: Описание: Z(N) Поразрядное объединение содержимого регистров F и W логической операцией “ИЛИ” с размещением результата в соответствии со значением указателя d Тактовых циклов: 1 Использование: Во всех семействах микроконтроллеров PIC (случай б — для семейства PIC 18) 18-6-767
274 Приложение Г. Система команд микроконтроллеров PIC LCALL Название: Long Call Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: Icall К [SP] <- PC; SP <- SP+1; PCL <- K(7:0) Нет “Длинный” переход 2 Только в микроконтроллерах семейства PIC17 LFSR Название: Load to FSR Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: Ifsr F, К FSR(F)«- К Нет Загрузка константы К в регистр, определенный регистром FSR 2 Только в микроконтроллерах семейства PIC18 MOVF Название: Move F Синтаксис: a) movf F, d б) movf F, d, a Операция: Если d=0, то W <— F Влияние на флаги: Описание: Z(N) Пересылка содержимого регистра F в регистр W в соответ- ствии со значением указателя d Тактовых циклов: 1 Использование: Во всех семействах микроконтроллеров PIC, кроме PIC17 (случай б — для семейства PIC18) MOVFF Название: Move Fs to Fd Синтаксис: Операция: Влияние на флаги: Описание: movff Fs, Fd Fd <—Fs Нет Пересылка содержимого регистра-источника Fs в регистр- приемник Fd Тактовых циклов: 2 Использование: Только в микроконтроллерах семейства PIC18 MOVFP Название: Move F to Primary Синтаксис: Операция: Влияние на флаги: movfp F, p p^F Нет
Алфавитный перечень команд 275 Описание: Пересылка содержимого регистра F в регистр из первичной области (00-1F) Тактовых циклов: 1 Использование: Только в микроконтроллерах семейства PIC 17 МО VLB Название: Move Literal to BSR Синтаксис: movlb К Операция: BSR(3:0)«— К Влияние на флаги: Нет Описание: Загрузка константы К в младший полубайт регистра BSR Тактовых циклов: 1 Использование: Только в микроконтроллерах семейств PIC 17 и PIC 18 MOVLR Название: Move Literal to BSR Синтаксис: movlr к Операция: BSR(7:4) <— К Влияние на флаги: Нет Описание: Загрузка константы К в старший полубайт регистра BSR Тактовых циклов: 1 Использование: Только в микроконтроллерах семейства PIC 17 MOVLW Название: Move Literal to W Синтаксис: movlw к Операция: W <— К Влияние на флаги: Нет Описание: Пересылка содержимого константы К в регистр W Тактовых циклов: 1 Использование: Во всех семействах микроконтроллеров PIC MOVPF Название: Move Primary to F Синтаксис: movpf p, F Операция: F <— p Влияние на флаги: Z Описание: Пересылка содержимого регистра из первичной области (00-1F) в регистр F Тактовых циклов: 1 Использование: Только в микроконтроллерах семейства PIC17 MOVWF Название: Move W to F Синтаксис: a) movwf F б)movwf F, а 18*
276 Приложение Г. Система команд микроконтроллеров PIC Операция: F <— W Влияние на флаги: Описание: Тактовых циклов: Использование: Нет Загрузка содержимого регистра W в регистр F 1 Во всех семействах микроконтроллеров PIC (случай б — для семейства PIC18) MULLW Название: Multiply Literal with W Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: PRODH:PROGL <— К * W Нет Умножение содержимого регистра W на константу К 1 Только в микроконтроллерах семейств PIC17 и PIC18 MULWF Название: Multiply W with F Синтаксис: a) mulwf F 6) mulwf F, a Операция: PRODH:PROGL «— F * W Влияние на флаги: Описание: Тактовых циклов: Использование: Нет Перемножение содержимого регистров W и F 1 Только в микроконтроллерах семейств PIC 17 (а) и PIC18 (б) NEGF Название: Negate F Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: negf F, а F <—-F С, DC, Z, OV, N Изменение знака содержимого регистра W 1 Только в микроконтроллерах семейства PIC18 NEGW Название: Negate W Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: negw F, s Если s=0, то F < W; W < W C, DC, Z, OV (N) Изменение знака содержимого регистра W 1 Только в микроконтроллерах семейства PIC17
Алфавитный перечень команд 277 NOP Название: No Operation Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: пор Нет Нет Пустая операция 1 Во всех семействах микроконтроллеров PIC OPTION Название: Move W to OPTION Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: option OPTION <- W Нет Загрузка содержимого регистра W в регистр OPTION 1 Только в микроконтроллерах PIC с 12-ти- и 14-тираз- рядным процессорным ядром POP Название: Pop register from stack Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: pop W «— Стек Нет Извлечение значения из стека в регистр W 2 Только в микроконтроллерах семейства PIC18 PUSH Название: Push register to stack Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: push Стек <— W Нет Помещение значения регистра W в стек 2 Только в микроконтроллерах семейства PIC18 RCALL Название: Routine Call Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: rcall К [SP] <- PC; SP <- SP+1; PC «— PC + 2 + К Нет Вызов подпрограммы по 11-тиразрядному адресу 2 Только в микроконтроллерах семейства PIC18
278 Приложение Г. Система команд микроконтроллеров PIC RESET Название: Reset Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: reset MCLR 0; MCLR<— 1 Нет Перезапуск микроконтроллера 1 Только в микроконтроллерах семейства PIC18 RETFIE Название: Return From Interrupt Синтаксис: a) retfie б)retfie в) retfie s Операция: a) GIE<— 1; SP «—SP-1; PC <—[SP]; 6) SP «— SP - 1; PC «— [SP]; GLTIND 0; в) SP <— SP - 1; PC <— [SP]; GIE <— 0. Если s=l, то восста- навливаются из стека регистры W, STATUS и BSR Влияние на флаги: Нет Описание: Тактовых циклов: Использование: Возврат из прерывания 2 Во всех микроконтроллеров PIC, кроме семейств с 12-ти- разрядным процессорным ядром: а) — 14-тиразрядное процессорное ядро; б) — семейство PIC 17; в) — семейст- во PIC18 RETLW Название: Return with Literal in W Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: retlw К W «— К; SP «— SP - 1; PC «— [SP] Нет Возврат из подпрограммы с загрузкой константы в регистр W 2 Во всех семействах микроконтроллеров PIC RETURN Название: Return from subroutine Синтаксис: a) return б)return, s Операция: a) SP <—SP-1; PC [SP]; 6) SP <— SP - 1; PC «— [SP]. Если s = 1, то восстановление из стека содержимого регистров W, STATUS и BSR Влияние на флаги: Нет Описание: Тактовых циклов: Использование: Возврат из подпрограммы 2 Во всех семействах микроконтроллеров PIC: б) — для се- мейства PIC18
Алфавитный перечень команд 279 RLCF Название: Rotate Left F through Сапу Синтаксис: a) rlcf F, d 6) rlcf F, d, a Операция: Если d=l, то F(7:1) <- F(6:0); F(0) «- С; C <- F(7). Если d=0, to W(7:l) <- F(6:0); W(0) <- С; C <- F(7). Влияние на флаги: C(N) Описание: Циклический сдвиг содержимого регистра F влево (через флаг переноса) и сохранение результата в соответствии со значением указателя d Тактовых циклов: 1 Использование: Только в микроконтроллерах семейств PIC17 (а) и PIC 18 (б) RLF Название: Rotate Left F through Carry Синтаксис: Операция: - rlf F, d Temp «— С; C «— (F » 7) & 1. Если d=l, to F «- (F « 1) + Temp, иначе W <— (F « 1) + Temp Влияние на флаги: C Описание: Циклический сдвиг содержимого регистра F влево (через флаг переноса) и сохранение результата в соответствии со значением указателя d Тактовых циклов: 1 Использование: Только в микроконтроллерах PIC с 12-ти- и 14-тираз- рядным процессорным ядром RLNCF Название: Rotate Left F (No Carry) Синтаксис: a) rlncf F, d 6) rlncf F, d, a Операция: Если d=l, to F(7:1) <- F(6:0); F(0) F(7). Если d=0, to W(7:1) F(6:0); W(0) <- F(7). Влияние на флаги: (N) Описание: Циклический сдвиг содержимого регистра F влево и сохра- нение результата в соответствии со значением указателя d Тактовых циклов: 1 Использование: Только в микроконтроллерах семейств PIC17 (а) и PIC 18 (б) RRCF Название: Rotate Right F through Carry Синтаксис: a) rrcf F, d 6) rrcf F, d, a Операция: Если d=l, to F(6:0) <- F(7:l); F(7) <- С; C <- F(0). Если d=0, to W(6:0) F(7:1); W(7) С; C F(0).
280 Приложение Г. Система команд микроконтроллеров PIC Влияние на флаги: C(N) Описание: Циклический сдвиг содержимого регистра F вправо (через флаг переноса) и сохранение результата в соответствии со значением указателя d Тактовых циклов: 1 Использование: Только в микроконтроллерах семейств PIC17 (а) и PIC18 (б) RRF Название: Rotate Right F through Carry Синтаксис: Операция: Temp «— С; C «— F & 1. Если d=l, to F «— (F » 1) + (Temp « 7), иначе W «— (F » 1) + (Temp « 7) Влияние на флаги: C Описание: Циклический сдвиг содержимого регистра F вправо (через флаг переноса) и сохранение результата в соответствии со значением указателя d Тактовых циклов: 1 Использование: Только в микроконтроллерах PIC с 12-ти- и 14-тираз- рядным процессорным ядром RRNCF Название: Rotate Right F (No Carry) Синтаксис: a) rrncf F, d 6) rrncf F, d, a Операция: Если d=l, to F(6:0) <- F(7:l); F(7) +- F(0). Если d=0, to W(6:0) +- F(7:1); W(7) <- F(0). Влияние на флаги: (N) Описание: Циклический сдвиг содержимого регистра F вправо и со- хранение результата в соответствии со значением указателя я Тактовых циклов: VI 1 Использование: Только в микроконтроллерах семейств PIC17 (а) и PIC18 (б) SETF Название: Set F Синтаксис: a) setf F, s б) setf F, s, а Операция: F «— OxOFF. Если s=0, то W <— OxOFF Влияние на флаги: Описание: Тактовых циклов: Использование: Нет Установка в лог. 1 всех разрядов регистра F 1 Только в микроконтроллерах семейств PIC17 (а) и PIC18 (б)
Алфавитный перечень команд 281 SLEEP Название: Go into standby mode Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: sleep WDT«—0 TD<- 1; PD<-0 Перевод микроконтроллера в режим ожидания 1 Во всех семействах микроконтроллеров PIC SUBFWB Название: Subtract F from W with Borrow Синтаксис: Операция: Влияние на флаги: Описание: subfwb F, d, a Если d=l, то W <— W-F - !C, иначе F <- W - F - !C C, DC, Z, OV, N Вычитание содержимого регистра F и переноса из содер- жимого регистра W с размещением результата в соответст- вии со значением указателя d Тактовых циклов: 1 Использование: Только в микроконтроллерах семейства PIC18 SUBLW Название: Subtract W from Literal Синтаксис: Операция: Влияние на флаги: Описание: Тактовых циклов: Использование: sublw К W К + (W Л OxOFF) + 1 C, DC, Z (OV, N) Вычитание содержимого регистра W из константы К 1 Во всех микроконтроллерах PIC, кроме семейств с 12-ти- разрядным процессорным ядром SVBWF Название: Subtract W from F Синтаксис: a) subwf F, d 6) subwf F, d, a Операция: Если d=l, to W«—F+(WA0x0FF)+l, иначе F«—F +(WA0x0FF)+l Влияние на флаги: Описание: C, DC, Z (N, OV) Вычитание содержимого регистра W из содержимого реги- стра F с размещением результата в соответствии со значе- нием указателя d Тактовых циклов: 1 Использование: Во всех семействах микроконтроллеров PIC (случай б — для семейства PIC18)
282 Приложение Г. Система команд микроконтроллеров PIC SUBWFB Название: Subtract W from F with Borrow Синтаксис: a) subwfb F, d 6) subwfb F, d, a Операция: Если d=l, to W «— F - W - !C, иначе F F - W - !C Влияние на флаги: Описание: C, DC, Z, OV (N) Вычитание содержимого регистра W из содержимого реги- стра F (с учетом переноса) с размещением результата в со- ответствии со значением указателя d Тактовых циклов: 1 Использование: Только в микроконтроллерах семейств PIC17 (а) и PIC18 (б) SWAPF Название: Swap nibbles in F Синтаксис: a) swapf F, d 6) swapf F, d, a Операция: Если d=l, to F <— ((F & OxOFO) » 4) + ((F & OxOOF) « 4), иначе W <— ((F & OxOFO)» 4) + ((F & OxOOF) « 4) Влияние на флаги: Нет Описание: Перемена местами старшего и младшего полубайтов реги- стра F с размещением результата в соответствии со значе- нием указателя d Тактовых циклов: 1 Использование: Во всех семействах микроконтроллеров PIC (случай б — для семейства PIC18) TABLRD Название: Table Read Синтаксис: a) tablrd t, i, F 6)tablrd Option Операция: a) F <— TBLATH; если t=l, то TBLAT <— ProgMem(TBLPTR). Если i=l, to TBLPTR <- TBLPTR + 1. б) Если Option = *, to TABLAT <— ProgMem(TBLPTR). Если Option = *+, to TABLAT <— ProgMem(TBLPTR); TBLPTR«—TBLPTR + 1. Если Option = to TABLAT <— ProgMem(TBLPTR); TBLPTR <—TBLPTR - 1. Если Option = +*, to TBLPTR <— TBLPTR + 1; TABLAT <- ProgMem(TBLPTR). Влияние на флаги: Нет Описание: Табличное чтение. В случае (а) — считывание через ре- гистр TBLATH одного из двух байтов содержимого ячейки памяти программ, определяемой указателем TBLPTR. В случае (б) — считывание содержимого ячейки памяти программ, определяемой табличным указателем TBLPTR Тактовых циклов: 2 или 3 Использование: Только в микроконтроллерах семейств PIC17 (а) и PIC18 (б)
Алфавитный перечень команд 283 TABLWT Название: Table Write Синтаксис: a) tablwt t, i, F 6)tablwt Option Операция: a) TBLATH F; если t=l, to TBLAT <- ProgMem(TBLPTR). Если i=l, to TBLPTR <- TBLPTR + 1. б) Если Option = *, to ProgMem(TBLPTR) <— TABLAT. Если Option = *+, to ProgMem(TBLPTR) <— TABLAT; TBLPTR <- TBLPTR + 1. Если Option = , to ProgMem(TBLPTR) «— TABLAT; TBLPTR <—TBLPTR - 1. Если Option = +*, to TBLPTR <— TBLPTR + 1; ProgMem(TBLPTR) <- TABLAT. Влияние на флаги: Нет Описание: Табличная запись. В случае (а) — запись через регистр TBLATH содержимого регистра F в ячейку памяти про- грамм, определяемой указателем TBLPTR. В случае (б) — запись содержимого табличного регистра в ячейку памяти программ, определяемую указателем TBLPTR. Если при- емником является встроенная память EEPROM, то команда завершается только по прерыванию. Тактовых циклов: 2 или несколько в случае EEPROM Использование: Только в микроконтроллерах семейств PIC17 (а) и PIC18 (б) TLRD Название: Table Register Read Синтаксис: Операция: Влияние на флаги: Описание: tlrd t, F Если t=l, to F <- TBLATH, иначе F <- TBLATL Нет Считывание в регистр F содержимого старшего или млад- шего байта табличного регистра Тактовых циклов: 1 Использование: Только в микроконтроллерах семейства PIC17 TLWT Название: Table Register Write Синтаксис: Операция: Влияние на флаги: Описание: tlwt t, F Если t=l, to TBLATH <- F, иначе TBLATL <- F Нет Запись содержимого регистра F в старший или младший байт табличного регистра Тактовых циклов: 1 Использование: Только в микроконтроллерах семейства PIC17
284 Приложение Г. Система команд микроконтроллеров PIC TSTFSZ Название: Test F, Skip if Zero Синтаксис: a) t s t f s z F 6) tstfsz F, a Операция: Если F = 0, то PC «— PC + 1 Влияние на флаги: Описание: Нет Если содержимое регистра F равно нулю, то пропускается следующая команда Тактовых циклов: 1(2) Использование: Только в микроконтроллерах семейств PIC17 (а) и PIC18 (б) TRIS Название: Move W to TRIS Синтаксис: Операция: Влияние на флаги: Описание: tris P TRIS(P) <- W Нет Пересылка содержимого регистра W в регистр управления порта TRIS Тактовых циклов: 1 Использование: Только в микроконтроллерах PIC с 12-ти- и 14-тираз- рядным процессорным ядром XORLW Название: Exclusive OR Literal with W Синтаксис: Операция: Влияние на флаги: Описание: xorlw К W <- WAK Z(N) Поразрядное объединение содержимого регистра W и кон- станты К логической операцией “Исключающее ИЛИ” Тактовых циклов: 1 Использование: Во всех семействах микроконтроллеров PIC XORWF Название: Exclusive OR W with F Синтаксис: a) xorwf F, d 6) xorwf F, d, a Операция: Если d=l, to F «— F л W, иначе W «— F л W Влияние на флаги: Описание: Z(N) Поразрядное объединение содержимого регистров F и W логической операцией “Исключающее ИЛИ” с сохранени- ем результата в соответствии со значением указателя d Тактовых циклов: 1 Использование: Во всех семействах микроконтроллеров PIC (случай б — для семейства PIC 18)
Команды по категориям 285 Команды по категориям Категория Команда Название 12-p. КОП 14-p. КОП PIC17 PIC18 Арифметиче- ские и логи- ческие addlw К Add Literal and W — X X X addwf F, d Add W and F X X X — addwf F, d, a — — — X addwfc F, d Add W and F with Carry X X X — addwfc F, d, a — — — X andlw К AND Literal with W X X X X andwf F, d AND W with F X X X — andwf F, d, a — — — X clrf F Clear F X X — — clrf F, s — — X — clrf F, a — — — X clrw Clear W X X — — comf F, d Complement F X X X — comf F, d, a — — — X daw F, s Decimal Amendment ofW — X — daw — — X dcfsnz F, d Decrement F, Skip if Not Zero — — X — dcfsnz F, d, a — __ — X decf F, d Decrement F X X X — decf F, d, a — — — X decfsz F, d Decrement F, Skip if Zero X X X — decfsz F, d, a — — — X incf F, d Increment F X X X — incf F, d, a — — X incfsz F, d Increment F, Skip if Zero X X X — incfsz F, d, a — — X infsnz F, d Increment F, Skip if Not Zero — — X — infsnz F, d, a — — — X iorlw К Inclusive OR Literal with W - - X X iorwf F, d Inclusive OR W with F X X X — iorwf F, d, a — — — X mullw К Multiply Literal with W — — X X mulwf F Inclusive OR W with F — X — mulwf F, a — — — X negf F, a Negate F — — — X negw W, s Negate W — — X — rlcf F, d Rotate Left F through Carry — — X — rlcf F, d, a — — — X rlf F, d Rotate Left F through Carry X X - - rlncf F, d Rotate Left F (No Carry) — — X — rlncf F, d, a - - X
286 Приложение Г. Система команд микроконтроллеров PIC Категория Команда Название 12-p. КОП 14-p. КОП PIC17 PIC18 rrcf F, d Rotate Right F through Carry — — X — rrcf F, d, a — — — X rrf F, d Rotate Right F through Carry X X - - rrncf F, d Rotate Right F (No Carry) — — X — rrncf F, d, a — — — X setf F, s Set F — X — setf F, s, a — — — X subfwb F, d, a Subtract F from W with Borrow - - - X sublw К Subtract W from Literal — X X X subwf F, d Subtract W from F X X X — subwf F, d, a — — X subwfb F, d Subtract W from F with Borrow — — X — subwfb F, d, a — — — X swapf F, d Swap nibbles in F X X X — swapf F, d, a — — X xorlw Exclusive OR Literal with W X X X X xorwf F, d Exclusive OR W with F X X X — xorwf F, d, a — — X Пересылки Ifsr F, К Load to FSR — — — X movf F, d Move F X X — — movf F, d, a — — — X movff Fs, Fd Move Fs to Fd — — — X movfp F, p Move F to Primary — — X — movlb К Move Literal to BSR — — X X movlr К Move Literal to BSR — X movlw К Move Literal to W X X X X movpf p, F Move Primary to F — X — movwf F Move W to F X X X — movwf F, a — — — X option Move W to OPTION X X — Pop Pop register from stack — — X push Push register to stack — — — X rcall К Routine Call — — — X retlw К Return With Literal in W X X X X tablrd t, i, F Table Read — — X — tablrd Option — — — X tablwt t, i, F Table Write — X — tablwt Option — — X tlrd t, F Table Register Read — — X — tlwt t, F Table Register Write — — X — tris P Move W to TRIS X X — —
Команды по категориям 287 Категория Команда Название 12-p. КОП 14-p. КОП PIC17 PIC18 Ветвления Ьс К Branch if Carry — — X bn К Branch if Negative — — X Ьпс К Branch if Not Carry — — — X bnn К Branch if Not Negative — — — X bnov К Branch if Not Overflow — — — X bnz К Branch if Not Zero — — — X bov К Branch if Overflow — — — X bra К Branch — — — X btfsc F, b Bit Test F, Skip if Clear X X X — btfsc F, b, а — — — X btfss F, b Bit Test F, Skip if Set X X X — btfss F, b, а — — — X bz К Branch if Zero — — X call А Call a Subroutine X X — — call К — — X — call К, s — — — X cpfseq F Compare F with W, Skip if Equal — X cpfseq F, а — — X cpfsgt F Compare F with W, Skip if Greater — — X — cpfsgt F, a — — — X cpfslt F Compare F with W, Skip if Less — — X — cpfslt F, a — — X goto A Call a Subroutine X X — — goto К — — X X Icall К Long Call — — X — retfie Return From Interrupt — X X — retfie s — — — X retlw К Return With Literal in W X X X X return Return From Interrupt X X X — return s — — — X tstfsz F Test F, Skip if Zero — — X — tstfsz F, a — __ — X Поразрядных операций bcf F, b Bit Clear F X X X — bcf F, b, a — — — X bsf F, b Bit Set F X X X —. bsf F, b, a — — — X btg F, b Bit Toggle F — — X — btg F, b, a — — — X rlcf F, d Rotate Left F through Carry — — X — rlcf F, d, a — — — X rlf F, d Rotate Left F through Carry X X - - rlncf F, d Rotate Left F (No Carry) — — X — rlncf F, d, a - - — X
288 Приложение Г. Система команд микроконтроллеров PIC Категория Команда Название 12-p. КОП 14-p. КОП PIC17 PIC18 rrcf F, d Rotate Right F through Carry — X — rrcf F, d, a — — X rrf F, d Rotate Right F through Carry X X - - rrncf F, d Rotate Right F (No Carry) X rrncf F, d, a — — X setf F, s Set F — X — setf F, s, a — — X Управления микрокон- троллером clrwdt Clear Watchdog Timer X X X X nop No Operation X X X X reset Reset — — X sleep Go into standby mode X X X X
Приложение Д Библиотечные функции и макроопределения Стандартные функции Математические abs Директива включения: Заголовок: Описание: Пример использования: #include <stdlib.h> int abs(int X) Возвращает модуль целого числа X. signed int target, actual; unsigned int error; error = abs(target-actual); acos Директива включения: Заголовок: Описание: #include <math.h> float acos(float X) Возвращает арккосинус вещественного числа X. Па- раметр X — в диапазоне -I... 1. Результат — в диапа- зоне 0...7Г. Пример использования: float val; val = acos(0.762); asin Директива включения: Заголовок: Описание: #include <math.h> float asin(float X) Возвращает арксинус вещественного числа X. Пара- метр X — в диапазоне -1... 1. Результат — в диапазо- не -7t/2...7t/2. Пример использования: float vail, val2; vail = asin(-1); val2 = asin(1); a tan Директива включения: Заголовок: Описание: #include <math.h> float atan(float X) Возвращает арктангенс вещественного числа X. Ре- зультат — в диапазоне —тс/2.. ,тс/2. Пример использования: float val; val = atan(1); 19 — 6-767
290 Приложение Д. Библиотечные функции и макроопределения atan2 Директива включения: Заголовок: Описание: #include <math.h> float atan2(float Y, float X) Возвращает арктангенс вещественного числа Y/Х. Ре- зультат — в диапазоне -л...it. Пример использования: float val; val = atan2(3.3, 4.5); ceil Директива включения: Заголовок: Описание: #include <math.h> float ceil(float X) Возвращает значение X, округленное до ближайшего целого. Пример использования: float val; val = ceil(3.57); //Результат: 4.00 cosh Директива включения: Заголовок: Описание: #include <math.h> float cosh(float X) Возвращает гиперболический косинус вещественного числа X (значение угла в радианах). Пример использования: float val; val = cosh(5.231); div Директива включения: Заголовок: Описание: #include <stdlib.h> div_t div(int num, int denom) Вычисляет значение num/denom и возвращает частное и остаток в структуре div_t, содержащей два поля типа int под названием quot И rem. Пример использования: div_t div_result; div_result = div(5, 2); //div_result.quot = 2; div_result.rem = 1 exp Директива включения: Заголовок: Описание: Пример использования: #include <math.h> float exp(float X) Возвращает значение ex. float val; val = exp (2.3); fabs Директива включения: Заголовок: Описание: Пример использования: #include <math.h> float fabs(float X) Возвращает модуль вещественного числа X. float vail, val2, res; res = fabs(vall-val2);
Стандартные функции 291 floor Директива включения: Заголовок: Описание: Пример использования: #include <math.h> float floor(float X) Возвращает целую часть вещественного числа X. float val; val = floor(4.567); //Результат = 4.000 fmod Директива включения: Заголовок: Описание: #include <math.h> float floor(float X, float Y) Возвращает остаток от деления вещественного числа X на вещественное число Y. Пример использования: float val; val = fmod(25.6, 8); //Результат = 1.600 frexp Директива включения: Заголовок: Описание: ttinclude <math.h> float frexp(float X, int *EXP) Возвращает мантиссу вещественного числа X в диапа- зоне 0,5... 1,0 или 0, если Х=0. В переменной, передан- ной по ссылке в качестве параметра ЕХР, сохраняется степень 2 экспоненты X. Если Х=0, в ЕХР также будет сохранен 0. Другими словами, вызову функции = frexp (х, е) соответствует соотношение х = у * 2 . Пример использования: float у; int е; у = fгехр(3.14159, &у); //Результат: у=0.785397, е=2 labs Директива включения: Заголовок: Описание: Пример использования: ttinclude <stdlib.h> long int absflong int X) Возвращает модуль целого числа X. signed long int target, actual; unsigned long int error; error = abs(target-actual); Idexp Директива включения: Заголовок: Описание: ttinclude <math.h> float Idexp(float X, int EXP) Возвращает X, умноженное на 2 в степени ЕХР, то есть, Х-2 Пример использования: float val; val = ldexp(2, 3); //Результат = 16.00 19*
292 Приложение Д. Библиотечные функции и макроопределения Idiv Директива включения: Заголовок: Описание: #include <stdlib.h> ldiv_t Idiv(long int num, long int denom) Вычисляет значение num/denom и возвращает частное и остаток в структуре Idiv t, содержащей два поля типа long int под названием quot и rem. Пример использования: ldiv_t res; res = ldiv(145000, 42560); //res.quot = 3; res.rem = 17320 log Директива включения: Заголовок: Описание: #include <math.h> float log(float X) Возвращает натуральный логарифм числа X. Если X <=0, возникнет ошибка. Пример использования: float val; val = log(5); //Результат = 1.609437 loglO Директива включения: Заголовок: Описание: #include <math.h> float loglO(float X) Возвращает логарифм числа X по основанию 10. Если X <=0, возникнет ошибка. Пример использования: float val; val = loglO(5); //Результат = 0.69897 modf Директива включения: Заголовок: Описание: ttinclude <math.h> float modf(float X, float *IPART) Разбивает число X на целую и дробную части. Дробная часть возвращается как число со знаком, а целая со- храняется в параметре-переменной IP ART. Пример использования: float int_part, fract_part; fract_part = modf (-45.7, &int_part)'; //fract_part = -0.7; int_part = -45.00 pow Директива включения: Заголовок: Описание: Пример использования: #include <math.h> float pow(float X, float Y) Возвращает число X, возведенное в степень Y. float val; val = pow(2, 3); //Результат = 8.000 sin Директива включения: Заголовок: Описание: Пример использования: #include <math.h> float sin(float X) Возвращает синус угла X, представленного в радианах. float val; val = sin(5,121); //Результат = -0.917673
Стандартные функции 293 sinh Директива включения: Заголовок: Описание: #include <math.h> float sinh(float X) Возвращает гиперболический синус угла X, представ- ленного в радианах. Пример использования: float val; val = sin(5,12); //Результат = 83.664702 sqrt Директива включения: Заголовок: Описание: #include <math.h> float sqrt(float X) Возвращает квадратный корень положительного числа X. Для отрицательного X результат не определен. Пример использования: float val; val = sqrt(6.4); //Результат = 2.529822 tan Директива включения: Заголовок: Описание: #include <math.h> float tan(float X) Возвращает тангенс угла X, представленного в радиа- нах. Пример использования: float val; val = tan(5.121); //Результат = -2.309565 tanh Директива включения: Заголовок: Описание: ttinclude <math.h> float tanh(float X) Возвращает гиперболический тангенс угла X, пред- ставленного в радианах. Пример использования: float val; val = tanh(5.121); //Результат = 0.999928 Для работы co строками atoi Директива включения: Заголовок: Описание: Пример использования: #include <stdlib.h> int atoi(char *str) Преобразует строку в целочисленное представление. #include <stdio.h> #include <stdlib.h> char str[6]; int x ; void main(void) { gets(str); x = atoi(str); printf("x=%d", x) ; }
294 Приложение Д. Библиотечные функции и макроопределения atol Директива включения: Заголовок: Описание: Пример использования: ♦include <stdlib.h> long int atol(char *str) Преобразует строку в целочисленное представление. #include <stdio.h> ♦include <stdlib.h> char str[6]; long int x; void main(void) { gets(str); x = atoi(str); printf("x=%d", x); } sprintf Директива включения: Заголовок: ♦include <stdio.h> char* sprintf(char *s, const char *fmt, [,argl, arg2...]) Описание: Копирует форматированный текст согласно формату, заданному строкой fmt (аналогично функции printf), и набору аргументов в строку s. В конце строки до- бавляется “\0”. Размер строки должен быть достаточ- ным для размещения в ней данных и символа “\0”. Пример использования: #include <stdio.h> char s[4 0 ] ; void main(void) { sprintf(s, "Dec: %lu, Hex: %LX", 50, 50); printf("%s", s) ; //Результат: "Dec: 50, Hex: 0032" } streat Директива включения: Заголовок: Описание: ♦include <string.h> char* strcat(char *dest, const char *src) Добавляет строку sre в конец строки dest. Строки не перекрываются. Строка dest должна иметь достаточ- ную длину. Функция возвращает указатель на dest Пример использования: ♦include <stdio.h> ♦include <string.h> char src[] = "String 1"; char dest[20] = "String 2"; void main(void) { strcat(dest, sre); printf("dest=%s", dest); //Результат: "dest = String 2Stringl"
Стандартные функции 295 strchr Директива включения: Заголовок: Описание: #include <string.h> char* strchr(char *src, char val) Возвращает указатель на первое вхождение символа val в строке src. Если символ не найден, возвращает- ся NULL Пример использования: #include <stdio.h> #include <string.h> char src[] = "String 1"; unsigned char c; void main(void) { c = getchar(); if (strchrfsrc, c) == NULL) printf("Character %c not found", c) ; } strcmp Директива включения: Заголовок: Описание: #include <string.h> int strcmp(const char *sl, const char *s2) Сравнивает две строки и возвращает число <0, если sl< s2; 0, если sl=s2 или число > 0, если si > s2 Пример использования: #include <stdio.h> #include <string.h> char sl[] = "String 1"; char s2[] = "String 2"; int res; void main(void) { res = strcmp(si, s2); printf("res=%d, res); // "res=-l" strcpy Директива включения: Заголовок: Описание: #include <string.h> char* strcpy(char *dest, const char *src) Копирует src в dest с учетом символа завершения строки “\0” Соответствующие области памяти не могут Пример использования: пересекаться. Функция возвращает указатель на dest #include <stdio.h> #include <string.h> char src[] = "Source string"; char dest[] = "Destination string"; void main(void) { strcpy(dest, src); printf("dest = %s", dest); //Результат: "dest = Source string\0ring"
296 Приложение Д. Библиотечные функции и макроопределения strlen Директива включения: Заголовок: Описание: Пример использования: strlwr Директива включения: Заголовок: Описание: Пример использования: strncmp Директива включения: Заголовок: Описание: Пример использования: #include <string.h> int strlen(const char *src) Возвращает количество символов в src, не включая символ конца строки “\0”. ttinclude <stdio.h> #include <string.h> char src[] = "Source string"; char c; void main(void) { c = getchar(); //Прием символа от USART if (memchr(src, c, strlen(src)) == NULL) printf("Character %c not found", c); } ttinclude <string.h> char* strlwr(char *s) Переводит символы строки s в нижний регистр и воз- вращает указатель на полученную строку. #include <stdio.h> ttinclude <string.h> char s[] = "Source STRING"; void main(void) { strlwr(s); printf("s = %s", s); //Результат: "s = source string" } #include <string.h> int strncmp(const char *sl, const char *s2, unsigned char len) Сравнивает первые len символов строк si и s2 и воз- вращает число <0, если sl< s2; 0, если sl=s2 или число > 0, если s 1 > s2 #include <stdio.h> #include <string.h> char sl[] = "String 1"; char s2[] = "String 2"; int res; void main(void) { res - strncmp(sl, s2, 6); printf("res=%d, res); //Результат: "res=0" }
Стандартные функции 297 strncpy Директива включения: Заголовок: #include <string.h> char* strncpy(char *dest, const char *src, unsigned char len) Описание: Копирует len символов строки sre в строку dest с учетом символа завершения строки “\0” Соответст- вующие области памяти не могут пересекаться. Функ- ция возвращает указатель на dest Пример использования: #include <stdio.h> #include <string.h> char src[] = "Source string"; char dest[] = "Destination string"; void main(void) { strncpy(dest, sre, 7); printf("dest = %s", dest); //Результат: "dest = Source tion string" } strrchr Директива включения: Заголовок: Описание: #include <string.h> char* strrchr(char *src, char val) Возвращает указатель на последнее вхождение симво- ла val в строке sre. Если символ не найден, возвраща- ется NULL Пример использования: #include <stdio.h> ttinclude <string.h> char src[] = "String 1"; unsigned char c; void main(void) { c = getchar() ; if (strrchr(sre, c) == NULL) printf("Character %c not found", c); } strstr Директива включения: Заголовок: Описание: ttinclude <string.h> char* strstr(char *sl, char *s2) Ищет первое вхождение подстроки s2 в строке s 1 (сим- волы “\0” не учитываются). Возвращает указатель на начало подстроки, или NULL, если подстрока не найде- на. Если s2 указывает на строку нулевой длины, воз- вращается si Пример использования: #include <stdio.h> #include <string.h> char sl[] = "Red, Green, Blue"; char s2[] = "Green";
298 Приложение Д. Библиотечные функции и макроопределения char *р; void main(void) { р = strstr(sl, s2); printf("New string", p); //"Green, Blue" } strtod Директива включения: Заголовок: Описание: Пример использования: strtol Директива включения: Заголовок: Описание: Пример использования: #include <stdlib.h> float strtod(char *s, char *endptr) Преобразует строку, на которую указывает s, в веще- ственное представление. Ведущие пробелы в строке игнорируются. Если параметр endpt г не равен null, он должен указывать на символ, следующий после по- следнего в преобразуемой строке #include <stdio.h> #include <stdlib.h> char str[] = "25.678 String"; float x; void main(void) { x = strtod(str, &str[6]); printf("x=%f", x); //Результат = 2.5678E01 } #include <stdlib.h> long int strtol(char *s, char *endptr, int base) Преобразует строку, на которую указывает s, в цело- численное представление по основанию base. Веду- щие пробелы в строке игнорируются. Если параметр endpt г не равен NULL, он должен указывать на сим- вол, следующий после последнего в преобразуемой строке. Если base > 10, то символу “А” соответствует число 10, символу “В” — число 11 и т.д. ttinclude <stdio.h> #include <stdlib.h> char str[] = "IF Hex string"; long int x; void main(void) { x = strtol(str, &str[2], 16); printf("x=%d", x); //Результат = 31
Стандартные функции 299 strtoul Директива включения: Заголовок: ttinclude <stdlib.h> unsigned long int strtol(char *s, char **endptr, int base) Описание: Преобразует строку, на которую указывает s, в цело- численное представление по основанию base. Веду- щие пробелы в строке игнорируются. Если параметр endptr не равен NULL, он должен указывать на сим- вол, следующий после последнего в преобразуемой строке. Если base > 10, то символу “А” соответствует число 10, символу “В” — число 11 и т.д. Пример использования: #include <stdio.h> #include <stdlib.h> char str[] = "1FA340D Hex string"; unsigned long int x; void main(void) { x = strtol(str, &str[7], 16); printf("x=%d", x); //Результат = 33174541 } Для работы с символами isalnum Директива включения: Заголовок: Описание: ttinclude <ctype.h> unsigned char isalnum(char C) Проверяет, является ли с буквенно-цифровым симво- лом. Если да, то возвращается 1 или код символа (зави- сит от компилятора), иначе — 0 Пример использования: ttinclude <stdio.h> #include <ctype.h> unsigned char tstl, tst2; void main(void) { tstl = isalnum('2'); tst2 = isalnum(2); printf("tstl=%d, tst2=%d", tstl, tst2); //Результат: "tstl=50, tst2=0" } isalpha Директива включения: Заголовок: Описание: #include <ctype.h> unsigned char isalpha(char C) Проверяет, является ли С буквенным символом (от “а” до “z”) в верхнем или нижнем регистре. Если да, то возвращается 1 или код символа (зависит от компиля- тора), иначе — 0
300 Приложение Д. Библиотечные функции и макроопределения Пример использования: #include <stdio.h> ttinclude <ctype.h> unsigned char tstl, tst2; void main(void) { tstl = isalpha('z'); tst2 = isalpha('2'); printf("tstl=%d, tst2=%d", tstl, tst2); //Результат: "tstl=122, tst2=0" } iscntrl Директива включения: Заголовок: Описание: ttinclude <ctype.h> unsigned char iscntrl(char C) Проверяет, является ли С управляющим символом (ко- ды в диапазоне от 0 до 31). Если да, то возвращается 1 или 255 (зависит от компилятора), иначе — 0 Пример использования: #include <stdio.h> #include <ctype.h> unsigned char tstl, tst2; void main(void) { tstl = iscntrl('\t'); tst2 = iscntrl('t'); printf("tstl=%d, tst2=%d", tstl, tst2); //Результат: "tstl=255, tst2=0" } isdigit Директива включения: Заголовок: Описание: #include <ctype.h> unsigned char isdigit(char C) Проверяет, является ли С символом цифры (от “0” до “9”). Если да, то возвращается 1 или код символа (за- висит от компилятора), иначе — 0 Пример использования: ttinclude <stdio.h> ttinclude <ctype.h> unsigned char tstl, tst2; void main(void) { tstl = isdigit('0') ; tst2 = isdigit('A'); printf("tstl=%d, tst2=%d", tstl, tst2); //Результат: "tstl=48, tst2=0" 1 isgraph Директива включения: Заголовок: #include <ctype.h> unsigned char isgraph(char C)
Стандартные функции 301 Описание: Проверяет, является ли С отображаемым символом (ко- ды в диапазоне от 33 до 127). Если да, то возвращается 1 или код символа (зависит от компилятора), иначе — 0 Пример использования: #include <stdio.h> #include <ctype.h> unsigned char tstl, tst2; void main(void) { tstl = isgraph('_'); tst2 = isgraph(' '); printf("tstl=%d, tst2=%d", tstl, tst2); //Результат: "tstl=95, tst2=0" } islower Директива включения: Заголовок: Описание: #include <ctype.h> unsigned char islowerfchar C) Проверяет, является ли с латинской буквой в нижнем регистре. Если да, то возвращается 1 или код символа (зависит от компилятора), иначе — 0 Пример использования: #include <stdio.h> #include <ctype.h> unsigned char tstl, tst2; void main(void) { tstl = islower(’a 1); tst2 = islower('A'); printf("tstl=%d, tst2=%d", tstl, tst2); //Результат: "tstl=97, tst2=0" } isprint Директива включения: Заголовок: Описание: #include <ctype.h> unsigned char isprint(char C) Проверяет, является ли С печатаемым символом (коды в диапазоне от 32 до 127). Если да, то возвращается 1 или код символа (зависит от компилятора), иначе — 0 Пример использования: #include <stdio.h> #include <ctype.h> unsigned char tstl, tst2; void main(void) { tstl = isprint(' ' ) ; tst2 = isprint('\n'); printf("tstl=%d, tst2=%d", tstl, tst2); //Результат: "tstl=32, tst2=0"
302 Приложение Д. Библиотечные функции и макроопределения ispunct Директива включения: Заголовок: Описание: #include <ctype.h> unsigned char ispunct(char C) Проверяет, является ли С символом-разделителем (лю- бой символ, кроме управляющих и буквенно-цифро- вых). Если да, то возвращается 1 или 255 (зависит от компилятора), иначе — 0 Пример использования: #include <stdio.h> #include <ctype.h> unsigned char tstl, tst2; void main(void) { tstl = ispunct('.'); tst2 = ispunct(' '); printf("tstl=%d, tst2=%d", tstl, tst2); //Результат: "tstl=255, tst2=0" } isspace Директива включения: Заголовок: Описание: #include <ctype.h> unsigned char isspace(char C) Проверяет, является ли С пробелом или знаком табуля- ции. Если да, то возвращается 1 или код символа (зави- сит от компилятора), иначе — 0 Пример использования: #include <stdio.h> #include <ctype.h> unsigned char tstl, tst2; void main(void) { tstl = isspace(' '); tst2 = isspace('_'); printf("tstl=%d, tst2=%d", tstl, tst2); //Результат: "tstl=32, tst2=0" } isupper Директива включения: Заголовок: Описание: #include <ctype.h> unsigned char isupper(char C) Проверяет, является ли С латинской буквой в верхнем регистре. Если да, то возвращается 1 или код символа (зависит от компилятора), иначе — 0 Пример использования: #include <stdio.h> #include <ctype.h> unsigned char tstl, tst2; void main(void) { tstl = isupper('A'); tst2 = isupper('a'); printf("tstl=%d, tst2=%d", tstl, tst2); //Результат: "tstl=65, tst2=0"
Стандартные функции 303 isxdigit Директива включения: Заголовок: Описание: #include <ctype.h> unsigned char isxdigit(char C) Проверяет, является ли С шестнадцатеричным симво- лом (“0”, “Г, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”, “а”, “b”, “с”, “d”, “е”, “f”, “А”, “В”, “С”, “D”, “Е” или “F”). Если да, то возвращается 1 или код символа (зависит от компилятора), иначе — 0 Пример использования: #include <stdio.h> #include <ctype.h> unsigned char tstl, tst2; void main(void) { tstl = isdigit('a'); tst2 = isdigit('h'); printf("tstl=%d, tst2=%d", tstl, tst2); //Результат: "tstl=97, tst2=0" } tolower Директива включения: Заголовок: Описание: Пример использования: #include <ctype.h> char tolower(char C) Преобразовывает символ С в нижний регистр #include <stdio.h> #include <ctype.h> void main(void) { printf("%c", tolower('A')); //Результат: "a" } toupper Директива включения: Заголовок: Описание: Пример использования: #include <ctype.h> char toupper(char C) Преобразовывает символ с в верхний регистр #include <stdio.h> ttinclude <ctype.h> void main(void) { printf("%c", toupper('a')); //Результат: "A" } Для работы со случайными числами rand Директива включения: #include <stdlib.h> Заголовок: int rand (void)
304 Приложение Д. Библиотечные функции и макроопределения Описание: Возвращает псевдослучайное число в диапазоне от 0 до значения RAND_MAX. Начальное число для генера- ции псевдослучайной последовательности задается с помощью функции srand (). По умолчанию, это число равно 1. Пример использования: for (int i=l; i<=10; i++) { printf("Number%d = %d", i, rand()); } srand Директива включения: Заголовок: Описание: #include <stdlib.h> void srand(unsigned int seed) Устанавливает начальное число для генерации псевдо- случайной последовательности Пример использования: for (int i=l; i<=10; i++) { srand(i); printf("Numbered = %d", i, rand()); } Для работы с памятью memchr Директива включения: Заголовок: Описание: #include <string.h> void* memchr(void *src, int val, size_t len) Просматривает первые len байтов области памяти, на которую указывает src, до символа val. Возвращает указатель на совпадающий байт или NULL, если сим- вол val в заданной области памяти найден не был Пример использования: #include <stdio.h> #include <string.h> char src[] = "Source string"; char c; void main(void) { c = getchar(); //Прием символа от USART if (memchr(src, c, strlen(src)) == NULL) printf("Character %c not found", c) ; } memстр Директива включения: Заголовок: #include <string.h> int memcmp(const void *sl, const void *s2, size_t len) Описание: Сравнивает первые len байтов областей памяти, кото- рые указывают si и s2, и возвращает число <0, если sl< s2; 0, если sl=s2 или число > 0, если si > s2
Стандартные функции 305 Пример использования: #include <stdio.h> #include <string.h> char sl[] = "String 1"; char s2[] = "String 2"; int resl, res2; void main(void) { resl = memcmp(sl, s2, 6); res2 = memcmp(sl, s2, strlen(si)); printf("resl=%d, res2=%d", resl, res2); //Результат: "resl=0, res2=-l" memcpy Директива включения: Заголовок: #include <string.h> void* memcpy(void *dest, const void *src, size_t len) Описание: Копирует len байтов из области памяти, на которую ссылается src, в область памяти, на которую ссылает- ся dest. Эти области памяти не могут пересекаться. Функция возвращает указатель на dest Пример использования: #include <stdio.h> #include <string.h> char src[] = "Source string"; char dest[] = "Destination string"; void main(void) { memcpy(dest, src, strlen(src)); printf("dest = %s", dest); //Результат: "dest = Source stringtring" memmove Директива включения: Заголовок: #include <string.h> void* memmove(void *dest, const void *src, size_t len) Описание: Копирует len байтов из области памяти, на которую ссылается src, в область памяти, на которую ссылает- ся dest. Эти области памяти могут пересекаться. Функция возвращает указатель на dest Пример использования: #include <stdio.h> ttinclude <string.h> char src[] = "Source string"; char dest[] = "Destination string"; void main(void) { memcpy(dest, src, strlen(src)); printf("dest = %s", dest); //Результат: "dest = Source stringtring" 20 — 6-767
306 Приложение Д. Библиотечные функции и макроопределения memset Директива включения: Заголовок: Описание: #include <string.h> void* memset(void *dest, int val, size_t len) Заполняет первые len байтов из области памяти, на которую ссылается dest, байтом val. Функция воз- вращает указатель на dest Пример использования: #include <stdio.h> #include <string.h> char dest[] = "Destination string"; void main(void) { memset(dest, 'X', 11); printf("dest = %s", dest); //Результат: "dest = XXXXXXXXXXX string" } Функции и макроопределения компилятора WinAVR Математические макроопределения м PI Директива включения: Определение: Описание: #include <math.h> #define M_PI 3.141592653589793238462643 Число 71. M_SQRT2 Директива включения: Определение: Описание: #include <math.h> #de£ine M_SQRT2 1.4142135623730950488016887 Квадратный корень из 2. RAND_MAX Директива включения: Определение: Описание: #include <stdlib.h> #define RAND_MAX 0x7FFF Наибольшее значение, которое может быть сгенериро- вано функцией rand (). RANDOM_MAX Директива включения: Определение: Описание: #include <stdlib.h> #define RANDOM_MAX 0x7FFFFFFF Наибольшее значение, которое может быть сгенериро- вано функцией random () . Математические функции inverse Директива включения: Заголовок: #include <math.h> double inverse(double X)
Функции и макроопределения компилятора WinAVR 307 Описание: Возвращает 1/Х. Пример использования: double val; val = inverse(0.25); //Результат = 4.00 isinf Директива включения: Заголовок: Описание: #include <math.h> int isinf(double X) Возвращает 1, если параметр X — положительное или отрицательное бесконечное значение. В противном случае возвращается 0. Пример использования: double X, Y; if (isinf(1/X)) printf("l/%f is the infinity", X); else Y = 1/X; square Директива включения: Заголовок: Описание: Пример использования: #include <math.h> double square(double X) Возвращает число X, возведенное в квадрат. double val; val = square(3); //Результат = 9.0000 Функции для работы со строками dtostre Директива включения: Заголовок: #include <stdlib.h> char* dtostre(double val, char *s, unsigned char prec, unsigned char flags) Описание: Преобразует значение val в строковое представление, которое сохраняется в переменной, переданной по ссылке в качестве параметра s. Преобразование вы- полняется в формате “ [-] d. dddeidd”, где количество символов после десятичной точки определяется пара- метром ргес. Если ргес = 0, то десятичная точка в полученной строке отсутствует. Параметр flags может содержать комбинацию следующих констант: - DTOSTRE_UPPERCASE — знак экспоненты отобража- ется в верхнем регистре (т.е. “Е”, а не “е”); - DTOSTRE_ALWAYS_SIGN -— перед положительными числами всегда вставляется пробел; - DTOSTRE_PLUS_SIGN — перед положительными чис- лами всегда вставляется знак “+”. Функция возвращает указатель на преобразованную строку S. Пример использования: char s [ ]; s = dtostre(32.567, s, 3, DTOSTRE_UPPERCASE | DTOSTRE_PLUS_SIGN); //s = "+3.257E+01" 20*
308 Приложение Д. Библиотечные функции и макроопределения dtostrf Директива включения: Заголовок: #include <stdlib.h> char* dtostrf(double val, char width, char prec, char *s) Описание: Преобразует значение val в строковое представление, которое сохраняется в переменной, переданной по ссылке в качестве параметра s. Преобразование вы- полняется в формате “[-]d.ddd”. Минимальная дли- на полученной строки, включая десятичную точку и возможный знак определяется параметром width, а количество символов после десятичной точки — па- раметром ргес. Функция возвращает указатель на преобразованную строку s. Пример использования: char s[]; s = dtostrf(-32.567, 6, 1, s); //s = "-32.60" itoa Директива включения: Заголовок: Описание: #include <stdlib.h> char* itoa(int val, char *s, int radix) Преобразует целочисленное значение val в строковое представление, которое сохраняется в переменной, пе- реданной по ссылке в качестве параметра s. Преобра- зование выполняется на основании значения параметра radix. Если radix == 2, то будет получено двоичное представление числа. Если radix > 10, то число 10 бу- дет обозначено в полученной строке символом “а”, число 11 — “Ь” и т.д. Функция возвращает указатель на преобразованную строку s. Пример использования: char s[]; s = itoa(26, s, 16); //s = "la" Itoa Директива включения: Заголовок: Описание: #include <stdlib.h> char* Itoa (long int val, char *s, int radix) Преобразует целочисленное значение val в строковое представление, которое сохраняется в переменной, пе- реданной по ссылке в качестве параметра s. Преобра- зование выполняется на основании значения параметра radix. Если radix == 2, то будет получено двоичное представление числа. Если radix > 10, то число 10 бу- дет обозначено в полученной строке символом “а”, число 11 -— “Ь” и т.д. Функция возвращает указатель на преобразованную строку s. Пример использования: char s[]; s = Itoa(2604623, s, 16); //s = "27be4f"
Функции и макроопределения компилятора WinAVR 309 snprintf Директива включения: Заголовок: #include <stdio.h> int snprintf(char *s, size_t n, const char *fmt, [,argl, arg2...]) Описание: Копирует форматированный текст (n символов) со- гласно формату, заданному строкой fmt (аналогично функции printf), и набору аргументов в строку s. В конце строки добавляется “\0”. Размер строки дол- жен быть достаточным для размещения в ней данных и символа “\0”. Функция возвращает количество симво- лов, которые были бы записаны в s, если бы там было достаточно места. Пример использования: #include <stdio.h> char s [ 4 0 ] ; void main(void) { snprintf(s,7,"Dec: %lu,Hex: %LX", 50,50); printf("%s", s); //Результат: "Dec: 50" } snprintf_P Директива включения: Заголовок: #include <stdio.h> int snprintf_P(char *s, size_t n, const char *fmt, [,argl, arg2...]) Описание: Копирует форматированный текст (n символов) со- гласно формату, заданному строкой fmt (аналогично функции printf), и набору аргументов в строку s. Строка fmt хранится в памяти программ. В конце строки добавляется “\0”. Размер строки должен быть достаточным для размещения в ней данных и символа “\0”. Функция возвращает количество символов, кото- рые были бы записаны в s, если бы там было доста- точно места. Пример использования: #include <stdio.h> char s[40]; const char fmt [ ] = "Dec: %lu, Hex: °oLX"; void main(void) { snprintf_P(s, 7, fmt, 50, 50); printf("%s", s); //Результат: "Dec: 50" } sprintf_P Директива включения: Заголовок: #include <stdio.h> int sprintf_P(char *s, const char *fmt, [,argl, arg2. . .] )
310 Приложение Д. Библиотечные функции и макроопределения Описание: Копирует форматированный текст согласно формату, заданному строкой fmt (аналогично функции printf), и набору аргументов в строку s. Строка fmt хранится в памяти программ. В конце строки добавляется “\0”. Размер строки должен быть достаточным для разме- щения в ней данных и символа “\0”. Пример использования: #include <stdio.h> char s [ 4 0] ; const char fmt[] = "Dec: %lu, Hex: %LX"; void main(void) { sprint f__P (s, fmt, 50, 50); printf("%s", s) ; //Результат: "Dec: 50, Hex: 0032" } strcasecmp Директива включения: Заголовок: #include <string.h> int strcasecmp(const char *sl, const char *s2) Описание: Сравнивает две строки без учета регистра символов и возвращает число <0, если sl< s2; 0, если sl=:s2 или число > 0, если si > s2 Пример использования: #include <stdio.h> #include <string.h> char sl[] = "STRING 1"; char s2[] = "String 2"; int res; void main(void) { res = strcasecmp(si, s2); printf("res=%d", res); //Результат: "res=-l" } strlcat Директива включения: Заголовок: #include <string.h> size__t strlcat (char *dest, const char *src, size_t siz) Описание: Добавляет строку src в конец строки de st размером siz. Функция возвращает значение strlen(scr) + MIN(siz, strlen(initial dist). Если возвращаемое значение Пример использования: >= siz, строка урезается. ttinclude <stdio.h> #include <string.h> char src[] = "String 1"; char dest[ll] = "String 2"; void main(void)
Функции и макроопределения компилятора WinAVR 311 { strlcat(dest, src, 14); printf("dest = %s", dest); //Результат: "dest = String 2Str", - в //dest поместились только 3 символа } striару Директива включения: Заголовок: #include <string.h> size_t strlcpy(char *dest, const char *src, size_t siz) Описание: Копирует строку src в строку dest размером siz (с учетом символа конца строки “\0”). Функция воз- вращает значение strlen(src). Если возвращаемое зна- чение >= siz, строка урезается. Пример использования: #include <stdio.h> #include <string.h> char src[] = "Source string"; char dest[] = "Destination string"; void main(void) { strlcpy(dest, src, strlen(src)); printf("dest = %s", dest); //Результат: "dest = Source string\Oring" strnaaseamp Директива включения: Заголовок: ttinclude <string.h> int strncasecmp(const char *sl, const char *s2, sizet len) Описание: Сравнивает первые len символов двух строк без учета регистра символов и возвращает число <0, если sl< s2; 0, если s 1 =s2 или число > 0, если si > s2 Пример использования: #include <stdio.h> #include <string.h> char sl[] = "STRING 1"; char s2[] = "String 2"; int res; void main(void) { res = strncasecmp(s1, s2, 6); printf("res=%d", res); // "res=0" strnaat Директива включения: Заголовок: #include <string.h> char* strncat(char *dest, const char *src, size t len)
312 Приложение Д. Библиотечные функции и макроопределения Описание: Добавляет первые len символов строки sre в конец строки dest. Строки не могут перекрываться, и стро- ка-приемник должна иметь достаточную длину. Функ- ция возвращает указатель на dest Пример использования: #include <stdio.h> #include <string.h> char src[] = "String 1"; char dest[20] = "String 2"; void main(void) { strncat(dest, sre, 3); printf("dest=%s" , dest); //Результат: "dest = String 2Str" } strnlen Директива включения: Заголовок: Описание: #include <string.h> size_t strnlen(const char *src, size_t len) Возвращает количество символов в sre, не включая символ конца строки “\0”, но не больше, чем len Пример использования: #include <stdio.h> #include <string.h> char sl[] = "String 1"; char s2[] = "String 2 - long"; char 11, 12; void main(void) { 11 = strnlen(sl, 10); //11 = 8 12 = strnlen(s2, 10); //12 = 10 printf("ll=%d, 12=%d", 11, 12); } strrev Директива включения: Заголовок: Описание: Пример использования: #include <string.h> char* strrev(char *s) Возвращает строку, обратную строке s. #include <stdio.h> #include <string.h> char sl[] = "0123456789"; void main(void) { strrev(sl); //si = "9876543210" printf("%s", si); } strsep Директива включения: Заголовок: #include <string.h> char* strsep (char *s,- const char *delim)
Функции и макроопределения компилятора WinAVR 313 Описание: Находит в строке s первое вхождение любого символа из строки delim и заменяет его символом “\0”. Адрес следующего символа после найденного разделителя присваивается указателю s. Функция возвращает ука- затель на исходное значение s. Пример использования: #include <stdio.h> #include <string.h> char s[] = "April 18, 2006"; char delim[] = void main(void) strsep(s, delim); //s = " 2006" printf("The year is %s", s+1); } strtok_r Директива включения: Заголовок: #include <string.h> char* strtok(char *s, char const *delim, char *last) Описание: Просматривает строку s в поиске первого символа, не входящего в строку delim. При этом предполагается, что si состоит из последовательности подстрок, раз- деленных символами из строки delim. Первый вызов функции вернет указатель на первый символ- разделитель, после которого в строке будет вставлен символ “\0”. Последующие вызовы функции будут воз- вращать следующие подстроки со вставкой вместо раз- делителя символа “\0” до тех пор, пока не будут про- смотрены все разделители. После этого возвращается NULL, last — это пользовательская строка, которая должна оставаться неизменной. Пример использования: #include <stdio.h> #include <string.h> char s[] = "(050)111-1111"; char delim[] = "()-"; void main(void) { char area_code[4]; char *prefix; char *postfix; strcpy(area_code,strtok_r(s,delim,NULL)); prefix = strtok(0, delim, NULL); postfix = strtok(0, delim, NULL); printf("Area code: %s\n\r", area_code); printf("Prefix: %s\n\r", prefix); printf("Postfix: %s\n\r", postfix);
314 Приложение Д. Библиотечные функции и макроопределения strupr Директива включения: Заголовок: Описание: #include <string.h> char* strupr(char *s) Переводит символы строки s в верхний регистр и воз- вращает указатель на полученную строку. Пример использования: #include <stdio.h> #include <string.h> char s[] = "Source STRING"; void main(void) { strupr(s) ; printf ("s = %s", s) ; //Результат: "s = SOURCE STRING" } ultoa Директива включения: Заголовок: #include <stdlib.h> char* ultoa(unsigned long int val, char *s, int radix) Описание: Преобразует целочисленное значение val в строковое представление, которое сохраняется в переменной, пе- реданной по ссылке в качестве параметра s. Преобра- зование выполняется на основании значения параметра radix. Если radix == 2, то будет получено двоичное представление числа. Если radix > 10, то число 10 бу- дет обозначено в полученной строке символом “а”, число 11 — “Ь” и т.д. Функция возвращает указатель на преобразованную строку s. Пример использования: char s [ ] ; s = ultoa(1456260463, s, 16); //s = "56CCC56F" utoa Директива включения: Заголовок: #include <stdlib.h> char* utoa(unsigned int val, char *s, int radix) Описание: Преобразует целочисленное значение val в строковое представление, которое сохраняется в переменной, пе- реданной по ссылке в качестве параметра s. Преобра- зование выполняется на основании значения параметра radix. Если radix == 2, то будет получено двоичное представление числа. Если radix > 10, то число 10 бу- дет обозначено в полученной строке символом “а”, число 11 — “Ь” и т.д. Функция возвращает указатель на преобразованную строку s. Пример использования: char s[]; s = utoa(65535, s, 16); //s = "FFFF"
Функции и макроопределения компилятора WinAVR 315 Макроопределения для работы со строками, хранимыми во флэш-памяти PGMJ? Директива включения: #include <avr/io.h> #include <avr/pgmspace.h> Определение: Описание: #define PGM_P const prog_char* Используется для объявления переменных, которые являются указателями на строку в памяти программ. pgm_read_byte Директива включения: #include <avr/io.h> #include <avr/pgmspace.h> Определение: #define pgm_read_byte(address_short) \ pgm_read_byte_near (address_short) Описание: Считывает байт из памяти программ по 16-тиразрядно- му (“ближнему”) адресу. pgm__r ead_by te_f ar Директива включения: #include <avr/io.h> #include <avr/pgmspace.h> Определение: #define pgm__read_byte__far (address_long) \ ELPM( (uint32_t) (address___long) ) Описание: Считывает байт из памяти программ по 32-хразрядно- му (“дальнему”) адресу. pgm__r ead_by te_near Директива включения: #include <avr/io.h> #include <avr/pgmspace.h> Определение: #define pgm_read__byte_near (address_short) \ LPM((uintl6_t)(address_short)) Описание: Считывает байт из памяти программ по 16-тиразрядно- му (“ближнему”) адресу. pgm_read_word Директива включения: #include <avr/io.h> #include <avr/pgmspace.h> Определение: #define pgm_read_word(address_short) \ pgm_read_word_near(address_short) Описание: Считывает слово из памяти программ по 16-тиразряд- ному (“ближнему”) адресу. pgm_read_word_f ar Директива включения: #include <avr/io.h> #include <avr/pgmspace.h>
316 Приложение Д. Библиотечные функции и макроопределения Определение: #define pgm_read_word_far(address_long) \ ELPM_word ( (uint32_t) (address__long) ) Описание: Считывает слово из памяти программ по 32-хразрядно- му (“дальнему”) адресу. pgm_rе ad_wo гd_near Директива включения: #include <avr/io.h> #include <avr/pgmspace.h> Определение: ftdefine pgm_read_word_near(address_short) \ LPM_word((uintl6_t)(address_short)) Описание: Считывает слово из памяти программ по 16-тиразряд- ному (“ближнему”) адресу. PGM_VOID_P Директива включения: #include <avr/io.h> #include <avr/pgmspace.h> Определение: Описание: {{define PGM_VOID_P const prog_void* Используется для объявления обобщенных указателей на некоторый объект в памяти программ. PSTR Директива включения: #include <avr/io.h> #include <avr/pgmspace.h> Определение: #define PSTR(s) \ ({static char c[] PROGMEM = (s); c;}) Описание: Используется для объявления статических указателей на некоторую строку в памяти программ. Функции для работы со строками, хранимыми во флэш- намяти memapy_P Директива включения: Заголовок: Описание: Пример использования: #include <avr/io.h> #include <avr/pgmspace.h> void* memcpy_P(void *dest, PGM__VOID_P src, size_t len) Копирует len байтов из области памяти программ, на которую ссылается src, в область памяти, на которую ссылается dest. Функция возвращает указатель на dest #include <stdio.h> #include <avr/io.h> #include <avr/pgmspace.h> const char src[] = "Source string"; char dest[] = "Destination string"; void main(void)
Функции и макроопределения компилятора WinAVR 317 memcpy_P(dest, src, strlen(src)); printf("dest = %s", dest); //Результат: "dest = Source stringtring" straasecmp_P Директива включения: Заголовок: Описание: Пример использования: strcat_P Директива включения: Заголовок: Описание: Пример использования: #include <avr/io.h> #include <avr/pgmspace.h> int strcasecmp_P(const char *sl, PGM_P s2) Сравнивает две строки без учета регистра символов и возвращает число <0, если sl< s2; 0, если sl=s2 или число > 0, если si > s2. Строка si размещена в памяти SRAM, а строка s2 — во флэш-памяти. #include <stdio.h> #include <avr/io.h> #include <avr/pgmspace.h> char sl[] = "STRING 1"; const char s2[] = "String 2"; int res; void main(void) { res = strcasecmp_P(si, s2); printf("res=%d", res); //Результат: "res=-l" } #include <avr/io.h> #include <avr/pgmspace.h> char* strcat_P (char *dest, PGM__P src) Добавляет строку src, размещенную в памяти про- грамм, в конец строки dest. Строка dest должна иметь достаточную длину. Функция возвращает указа- тель на dest #include <stdio.h> #include <avr/io.h> #include <avr/pgmspace.h> const char src[] = "String 1"; char dest[20] = "String 2"; void main(void) { strcat__P (dest, src); printf("dest=%s", dest); //Результат: "dest = String 2Stringl"
318 Приложение Д. Библиотечные функции и макроопределения stramp_P Директива включения: #include <avr/io.h> #include <avr/pgmspace.h> Заголовок: Описание: int strcmp_P(const char *sl, PGM_P s2) Сравнивает две строки (s2 размещена в памяти про- грамм) и возвращает число <0, если sl< s2; 0, если s 1 =s2 или число > 0, если si > s2 Пример использования: #include <stdio.h> #include <avr/io.h> #include <avr/pgmspace.h> char sl[] = "String 1"; const char s2[] = "String 2"; int res; void main(void) { res = strcmp_P(sl, s2); printf("res=%d, res); // "res=-l" } strcpy_P Директива включения: #include <avr/io.h> #include <avr/pgmspace.h> Заголовок: Описание: char* strcpy_P(char *dest, PGM_P sre) Копирует sre из памяти программ в dest с учетом символа завершения строки “\0”. Функция возвращает указатель на dest Пример использования: #include <stdio.h> #include <avr/io.h> #include <avr/pgmspace.h> const char src[] = "Source string"; char dest[] = "Destination string"; void main(void) { strcpy_P(dest, sre); printf("dest = %s", dest); //Результат: "dest = Source string\0ring" } strlaat__P Директива включения: ^include <avr/io.h> #include <avr/pgmspace.h> Заголовок: size_t strlcat_P(char *dest, PGM_P sre, size_t siz) Описание: Добавляет строку sre из памяти программ в конец строки dest размером siz. Функция возвращает зна- чение strlen(scr) + MIN(siz, strlen(initial dist). Если воз- вращаемое значение >= siz, строка урезается.
Функции и макроопределения компилятора WinAVR 319 Пример использования: #include <stdio.h> #include <avr/io.h> #include <avr/pgmspace.h> const char src[] = "String 1"; char dest[11] = "String 2"; void main(void) { strlcat_P(dest, src, 14); printf("dest = %s", dest); //Результат: "dest = String 2Str", - в //dest поместились только 3 символа } strlcpy_P Директива включения: #include <avr/io.h> #include <avr/pgmspace.h> Заголовок: size__t strlcpy_P (char *dest, PGM_P src, size_t siz) Описание: Копирует строку src из памяти программ в строку dest в памяти SRAM размером siz (с учетом символа конца строки “\0”). Функция возвращает значение strlen P(src). Если возвращаемое значение >= siz, строка урезается. Пример использования: #include <stdio.h> #include <avr/io.h> #include <avr/pgmspace.h> const char src[] = "Source string"; char destf] = "Destination string"; void main(void) { strlcpy_P(dest, src, strlen_P(src)); printf("dest = %s", dest); //Результат: "dest = Source string\Oring" strlen_P Директива включения: #include <avr/io.h> #include <avr/pgmspace.h> Заголовок: Описание: int strlen_P(PGM_P src) Возвращает количество символов в src, не включая символ конца строки “\0”. Пример использования: #include <stdio.h> #include <avr/io.h> #include <avr/pgmspace.h> const char src[] = "Source string"; cigar dest [ ] = "Destination string"; void main(void) { strlcpy_P(dest, src, strlen_P(src)); printf("dest = %s", dest); //Результат: "dest_= Source string\Oring"
320 Приложение Д. Библиотечные функции и макроопределения s trncasecmp_P Директива включения: #include <avr/io.h> #include <avr/pgmspace.h> Заголовок: int strncasecmp_P(const char *sl, PGM_P s2, size_t len) Описание: Сравнивает первые len символов двух строк без учета регистра символов и возвращает число <0, если sl< s2; 0, если sl=s2 или число > 0, если si > s2. Строка si — в памяти SRAM, строка s2 — во флэш-памяти Пример использования: #include <stdio.h> #include <avr/io.h> #include <avr/pgmspace.h> char sl[] = "STRING 1"; const char s2[] = "String 2"; int res; void main(void) { res = strncasecmp_P(s1, s2, 6); printf("res=%d", res); // "res=0" } strncat Р Директива включения: #include <avr/io.h> #include <avr/pgmspace.h> Заголовок: char* strncat_P(char *dest, PGM_P src, size_t len) Описание: Добавляет первые len символов строки src из памяти программ в конец строки dest в памяти SRAM. Стро- ка-приемник должна иметь достаточную длину. Функ- ция возвращает указатель на dest. Пример использования: #include <stdio.h> #include <avr/io.h> #include <avr/pgmspace.h> const char src[] = "String 1"; char dest[20] = "String 2"; void main(void) { strncat_P(dest, src, 3) ; printf("dest=%s", dest); //Результат: "dest = String 2Str" } strncmp_P Директива включения: #include <avr/io.h> #include <avr/pgmspace.h> Заголовок: int strncmp_P(const char *sl, PGM_P s2, size t len)
Функции и макроопределения компилятора WinAVR 321 Описание: Сравнивает первые len символов строк si и s2 и воз- вращает число <0, если sl< s2; 0, если sl=s2 или число > 0, если si > s2. Строка si — в памяти SRAM, строка s2 — во флэш-памяти. Пример использования: #include <stdio.h> #include <avr/io.h> #include <avr/pgmspace.h> char sl[] = "String 1"; const char s2[] = "String 2"; inr res; void main(void) { res = strncmp_P(si, s2, 6); printf("res=%d, res); //Результат: "res=0" 1 strncpy_P Директива включения: #include <avr/io.h> #include <avr/pgmspace.h> Заголовок: char* strncpy_P(char *dest, PGM_P src, unsigned char len) Описание: Копирует len символов строки src из памяти про- грамм в строку dest в памяти SRAM с учетом симво- ла завершения строки “\0”. Функция возвращает указа- тель на dest Пример использования: #include <stdio.h> #include <avr/io.h> #include <avr/pgmspace.h> const char src[] = "Source string"; char destf] = "Destination string"; void main(void) { strncpy_P(dest, src, 7); printf("dest = %s", dest); //Результат: "dest = Source tion string" 1 Функции для работы с символами isascii Директива включения: Заголовок: Описание: #include <ctype.h> int isascii(char C) Проверяет, соответствует ли С набору символов ASCII. Если да, то возвращается 128, иначе — 0 Пример использования: #include <stdio.h> #include <ctype.h> 21-6-767
322 Приложение Д. Библиотечные функции и макроопределения int tstl, tst2; void main(void) { tstl = isascii('W'); tst2 = isascii('ю; printf("tstl=%d, tst2=%d", tstl, tst2); //Результат: "tstl=128, tst2=0" } isblank Директива включения: Заголовок: Описание: #include <ctype.h> int isblank(char C) Проверяет, является ли С пробелом или символом та- буляции. Если да, то возвращается код символа, ина- че — 0 Пример использования: #include <stdio.h> #include <ctype.h> unsigned char tstl, tst2; void main(void) { tstl = isblank(' '); tst2 = isblank('_'); printf("tstl=%d, tst2=%d", tstl, tst2); //Результат: "tstl=32, tst2=0” } toascii Директива включения: Заголовок: Описание: ^include <ctype.h> unsigned char toascii(int C) Преобразовывает С в семиразрядное значение типа un- signed char, соответствующее таблице ASCII, путем очистки старших разрядов Пример использования: ^include <stdio.h> #include <ctype.h> unsigned char tstl, tst2; void main(void) tstl = toascii('A'); //Код 65 = 01000001 tst2 = toascii(193); //Код 193 = 11000001 printf("tstl=%c, tst2=%c", tstl, tst2); //Результат: "tstl=A, tst2=A" } Макроопределения для организации ввода/вывода FILE Директива включения: Определение: Описание: #include <stdio.h> #define FILE struct file Структура, используемая в различных стандартных функциях ввода/вывода.
Функции и макроопределения компилятора WinAVR 323 get с Директива включения: Определение: Описание: getchar Директива включения: Определение: Описание: putc Директива включения: Определение: Описание: putchar Директива включения: Определение: Описание: stderr Директива включения: Определение: Описание: stdin Директива включения: Определение: Описание: stdout Директива включения: Определение: Описание: #include <stdio.h> #define getc (__stream) fgetc(____stream) Используется для “быстрой” макрореализации функ- циональности, идентичной вызову fgetc (). #include <stdio.h> #define getchar() fgetc(stdin) Считывает символ из стандартного потока stdin. #include <stdio.h> #define putc(___c, __stream) \ fputc(__c, ___stream) Используется для “быстрой” макрореализации функ- циональности, идентичной вызову fputc (). ^include <stdio.h> #define putchar(___c) fputc(____c, stdout) Выдает символ в стандартный поток stdout. #include <stdio.h> # define stderr (__iob[2]) Поток, предназначенный для вывода ошибок. Если от- дельно не назначен с помощью функции fdevopen(), идентичен потоку stdout. #include <stdio.h> # define stdin (___iob[0]) Поток, используемый в качестве входного упрощенны- ми функциями, которые не принимают параметра stream. В качестве этого потока будет назначен пер- вый поток, открытый на чтение с помощью функции fdevopen() . #include <stdio.h> # define stdout (__iob[l]) Поток, используемый в качестве выходного упрощен- ными функциями, которые не принимают параметра stream. В качестве этого потока (а также stderr) бу- дет назначен первый поток, открытый на запись с по- мощью функции fdevopen () . 21*
324 Приложение Д. Библиотечные функции и макроопределения Функции ввода/вывода clearerr Директива включения: Заголовок: Описание: #include <stdio.h> void clearerr(FILE* stream) Сбрасывает флаги ошибки и конца файла потока stream. Пример использования: clearerr(stderr); fclose Директива включения: Заголовок: Описание: #include <stdio.h> int fclose(FILE* stream) Закрывает поток stream. Дальнейший ввод/вывод для этого потока невозможен. В случае успешного выпол- нения операции, функция возвращает 0 Пример использования: fclose(stderr); fdevopen Директива включения: Заголовок: #include <stdio.h> FILE* fdevopen(int(* put)(char), int(* get)(void), int opts) Описание: Открывает поток для некоторого устройства. В случае успеха возвращается указатель на структуру открытого потока. Если передается указатель на функцию put, то поток открывается на запись. Функция put должна принимать в качестве параметра один символ для запи- си в устройство и возвращать 0 в случае успешного вы- вода. Если передается указатель на функцию get, то поток открывается на чтение. Функция get не должна прини- мать параметров и возвращать символ, принятый от устройства. Об ошибке говорит возврат значения -1. Если передаются указатели одновременно на функцию put и get, то поток открывает на чтение/запись. Первый поток, открытый на чтение, назначается в каче- стве stdin, а первый, открытый на запись, — в качест- ве stdout и stderr. Третий параметр opts зарезервирован для использова- ния в будущем. Его значение роли не играет. Пример использования: #include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <avr/io.h> #define SYSCLK 14745600UL //Fosc в Гц #ifndef UCSRB # ifdef UCSR1A # define UCSRA UCSR1A
Функции и макроопределения компилятора WinAVR 325 # define UCSRB UCSR1B # define UBRR UBRR1L # define UDR UDR1 # else # define UCSRA USR # define UCSRB UCR # endif #endi f #ifndef UBRR # define UBRR UBRRL #endif int uart_putchar(char c) { if (c == '\n') uart_putchar('\r'); loop_until_bit_is__set (UCSRA, UDRE) ; UDR = c; return 0; } void main(void) { UCSRB = _BV(TXEN); //Активизируем вывод UBRR = (SYSCLK/(16*9600UL))-1; //9600 бод fdevopen(uart_putchar, NULL, 0); feof Директива включения: Заголовок: Описание: Пример использования: ^include <stdio.h> int feof(FILE* stream) Проверяет состояние флага “end-of-file” потока stream if (feof(stdin)) printf("The end..."); ferror Директива включения: Заголовок: Описание: Пример использования: #include <s'tdio.h> int ferror(FILE* stream) Проверяет состояние флага ошибки потока stream. if (ferror(stdin)) printf("The error..."); fgetc Директива включения: Заголовок: Описание: #include <stdio.h> int fgetc(FILE* stream) Считывает символ из потока stream. Возвращает счи- танный символ или -1 в случае ошибки. Пример использования: char с; с = fgetc(stdin);
326 Приложение Д. Библиотечные функции и макроопределения fgets Директива включения: Заголовок: Описание: #include <stdio.h> char* fgets(char* str, int size, FILE* stream) Считывает size-1 байт из потока stream (или пока не будет встречен символ новой строки) и сохраняет счи- танные символы в буфере, на который указывает str. Если при этом не возникло ошибок, строка будет за- вершена символом “\0”. В случае ошибки функция воз- вращает NULL и устанавливает флаг ошибки потока stream. Пример использования: char s [20] ; fgets(s, 19, stdin); fprintf Директива включения: Заголовок: #include <stdio.h> int fprintf(FILE* stream, const char* fmt, [argl, arg2, .. . ]) Описание: Выполняет вывод в поток stream форматированной строки fmt в соответствии со значениями списка пара- метров (см. описание функции printf в главе 3). Пример использования: int х=100; fprintf(stdout, "X=%d", х) ; fprintf_P Директива включения: Заголовок: #include <stdio.h> int fprintf_P(FILE* stream, const char* fmt, [argl, arg2, ...]) Описание: Выполняет вывод в поток stream форматированной строки fmt (хранится в памяти программ) в соответст- вии со значениями списка параметров (см. описание функции printf в главе 3). Пример использования: const char s[] = "X=%d"; int x=100; fprintf(stdout, s, x); fputc Директива включения: Заголовок: Описание: #include <stdio.h> int fputc(int C, FILE* stream) Выводит в поток stream символ С. Возвращает символ или -1 в случае ошибки. Пример использования: char s[] = "0123456789"; for (int i=0; i<10; i++) { fputc(s[i], stdout);
Функции и макроопределения компилятора WinAVR 327 fputs Директива включения: Заголовок: Описание: #include <stdio.h> int fputs(const char *s, FILE* stream) Выводит в поток stream строку s. Возвращает 0 в случае успешного вывода или -1 в случае ошибки. Пример использования: char s[] = "0123456789"; fputs(s, stdout); fputs_P Директива включения: Заголовок: Описание: #include <.stdio.h> int fputs_P(const char *s, FILE* stream) Выводит в поток stream строку s из памяти программ. Возвращает 0 в случае успешного вывода или -1 в случае ошибки. Пример использования: const char s[] = "0123456789"; fputs(s, stdout); f read Директива включения: Заголовок: #include <stdio.h> size_t fread(void *ptr, size_t size, size_t nmemb, FILE* stream) Описание: Считывает nmemb объектов no size байт каждый из по- тока stream в буфер, на который указывает ptr. Воз- вращает количество успешно считанных объектов. Пример использования: char s [21]; fread(s, 2, 10, stdin); fscanf Директива включения: Заголовок: #include <stdio.h> int fscanf(FILE* stream, const char *fmt, [argl, arg2, ...]) Описание: Выполняет форматированный ввод в соответствии со строкой fmt (см. описание функции printf в главе 3), считывая данные из потока stream. Результаты преоб- разования сохраняются в переменных, переданных в функцию по ссылке в качестве параметров argl, arg2... Любые параметры, для которых не заданы па- раметры форматирования в строке fmt, воспринимают- ся как текст. Пример использования: char s[20]; int i; char с; float x; fscanf(stdin, "%s%d%c%f", &s, &i, &c, &x) ;
328 Приложение Д. Библиотечные функции и макроопределения fscanf_P Директива включения: Заголовок: #include <stdio.h> int fscanf_P(FILE* stream, const char *fmt, [argl, arg2, ...]) Описание: Выполняет форматированный ввод в соответствии со строкой fmt (см. описание функции printf в главе 3), считывая данные из потока stream. Строка fmt хра- нится в памяти программ. Результаты преобразования сохраняются в переменных, переданных в функцию по ссылке в качестве параметров argl, arg2... Любые па- раметры, для которых не заданы параметры формати- рования в строке fmt, воспринимаются как текст. Пример использования: const char fmt [ ] = "%s%d%c%f"; char s [ 20]; int i; char c; float x; fscanf(stdin, fmt, &s, &i, &c, &x) ; fwrite Директива включения: Заголовок: #include <stdio.h> size_t fwrite(const void *ptr, size_t size, size__t nmemb, FILE* stream) Описание: Записывает nmemb объектов no size байт каждый в по- ток stream из буфера, на который указывает ptr. Воз- вращает количество успешно записанных объектов. Пример использования: char s[] = "01234567890123456789"; fwrite (s, 10, 2, stdout); gets Директива включения: Заголовок: Описание: ttinclude <stdio.h> char* gets(char *str) Считывает строку из потока stdin, пока не будет встречен символ новой строки, и сохраняет считанные символы в буфере, на который указывает str. В случае ошибки функция возвращает NULL и устанавливает флаг ошибки потока stdin. Пример использования: char s [ 20] ; gets (s); printf Директива включения: Заголовок: Описание: #include <stdio.h> int printf(const char* fmt, [argl, arg2, ...]) Выполняет вывод в поток stderr форматированной строки fmt в соответствии со значениями списка пара- метров (см. описание функции printf в главе 3). Воз- вращает количество записанных символов или -1 в слу- чае ошибки. Пример использования: int х=100; char s [] = "Just a string"; printf("X=%d, S=%s\n\r", x, s);
Функции и макроопределения компилятора WinAVR 329 printf_P Директива включения: Заголовок: Описание: #include <stdio.h> int printf_P(const char* fmt, [argl,arg2,...j) Выполняет вывод в поток stderr форматированной строки fmt (хранится в памяти программ) в соответст- вии со значениями списка параметров (см. описание функции printf в главе 3). Возвращает количество за- писанных символов или —1 в случае ошибки. Пример использования: int х=100; char s[] = "Just a string"; const char fmt [ ] = "X=%d, S=%s\n\r"; printf_P(fmt, x, s); puts Директива включения: Заголовок: Описание: #include <stdio.h> int puts(const char *s) Выводит в поток stdout строку s. Возвращает 0 в случае успешного вывода или -1 в случае ошибки. Пример использования: char s[] = "0123456789"; puts(s); puts_P Директива включения: Заголовок: Описание: #include <stdio.h> int puts_P(const char *s) Выводит в поток stdout строку s, хранимую в памяти программ. Возвращает 0 в случае успешного вывода или -1 в случае ошибки. Пример использования: const char s[] = "0123456789"; puts(s); scant Директива включения: Заголовок: Описание: #include <stdio.h> int scanf(const char *fmt, [argl, arg2, ...]) Выполняет форматированный ввод в соответствии со строкой fmt (см. описание функции printf в главе 3), считывая данные из потока stdin. Результаты преобра- зования сохраняются в переменных, переданных в функцию по ссылке в качестве параметров argl, arg2... Любые параметры, для которых не заданы па- раметры форматирования в строке fmt, воспринимают- ся как текст. Пример использования: char s [20] ; int i; char с; float x; scanf("%s%d%c%f", &s, &i, &c, &x);
330 Приложение Д. Библиотечные функции и макроопределения scanf_Р Директива включения: Заголовок: Описание: #include <stdio.h> int scanf_P(const char *fmt, [argl,arg2, ...]) Выполняет форматированный ввод в соответствии со строкой fmt (см. описание функции printf в главе 3), считывая данные из потока stdin. Строка fmt хранит- ся в памяти программ. Результаты преобразования со- храняются в переменных, переданных в функцию по ссылке в качестве параметров argl, arg2... Любые па- раметры, для которых не заданы параметры формати- рования в строке fmt, воспринимаются как текст. Пример использования: const char fmt[] = "%s%d%c%f"; char s [20]; int i; char c; float x; scanf_P(fmt, &s, &i, &c, &x); sscanf Директива включения: Заголовок: #include <stdio.h> int sscanf(const char *buf, const char *fmt, [argl, arg2, . . . ] ) Описание: Выполняет форматированный ввод в соответствии со строкой fmt (см. описание функции printf в главе 3), считывая данные из буфера, на который указывает buf. Результаты преобразования сохраняются в переменных, переданных в функцию по ссылке в качестве парамет- ров argl, arg2... Пример использования: char с; float х; sscanf("А,3.3", "%c,%f", &i, &c, &x) ; //с = 'A'; x = 3.3 sscanfР Директива включения: Заголовок: #include <stdio.h> int sscanf_P(const char *buf, const char *fmt, [argl, arg2, . . . ] ) Описание: Выполняет форматированный ввод в соответствии со строкой fmt (см. описание функции printf в главе 3), считывая данные из буфера, на который указывает buf. Строка fmt хранится в памяти программ. Результаты преобразования сохраняются в переменных, передан- ных в функцию по ссылке в качестве argl, arg2... Пример использования: const char fmt[] = "%c,%f"; int i; float x; sscanf("A,3.3", fmt, &c, &x) ; //с = 'A'; x = 3.3
Функции и макроопределения компилятора WinAVR 331 Функции управления микроконтроллером abort Директива включения: Заголовок: Описание: Пример использования: #include <stdlib.h> void abort(void) Вызывает прерывание работы программы. if (i<0) abort(); _del ау_1 оор__1 Директива включения: Заголовок: Описание: #include <avr/delay.h> void _delay_loop_l(uint8_t count) Задержка, пока счет, начиная с 0, не достигнет восьми- разрядного значения count. Пример использования: _delay_loop_l(1000); _de 1 ау_1 о ор_2 Директива включения: Заголовок: Описание: #include <avr/delay.h> void _delay_loop_2(uint16_t count) Задержка, пока счет, начиная с 0, не достигнет 16-ти- разрядного значения count. Пример использования: _delay_loop_2(100000); exit Директива включения: Заголовок: Описание: Пример использования: #include <stdlib.h> void abort(int status) Прерывает работу приложения. if (i<0) exit (0) ; set_sleep_mode Директива включения: Заголовок: Описание: #include <avr/sleep.h> void set__sleep_mode (uint8_t mode) Устанавливает разряды регистра MCUCR для выбора “спящего”режима. Возможные значения для mode: - SLEEP MODE ADC - режим пониженного шума от АЦП; - SLEEP_MODE_EXT_STANDBY - расширенный режим ожидания; - SLEEP_MODE_IDLE - ЖДУЩИЙ режим; - SLEEP_MODE_PWR_DOWN - режим пониженного энерго- потребления; - SLEEP_MODE_PWR_SAVE - режим энергосбережения; - SLEEP_MODE_STANDBY - режим ожидания. Пример использования: set__sleep_mode (SLEEP_MODE_IDLE) ;
332 Приложение Д. Библиотечные функции и макроопределения sleep_mode Директива включения: Заголовок: Описание: #include <avr/sleep.h> void sleep_mode(void) Переводит микроконтроллер в “спящий”режим. Выход устройства из этого режима зависит от установки, вы- полненной с помощью функции set sleep mode. Пример использования: set_sleep_mode(SLEEP_MODE_IDLE); sleep_mode(); Функции для работы с таймерами микроконтроллера timer0_s ource Директива включения: Заголовок: Описание: #include <avr/timer.h> void timerO source(unsigned_int sre) Установка источника тактовых сигналов таймера Т/СО. Возможные значения параметра sre: - STOP — останов; - СК — режим “Таймер”, такт = такт системной синхро- низации; - СК8 — режим “Таймер”, такт = такт системной син- хронизации / 8; - СК64 — режим “Таймер”, такт = такт системной син- хронизации / 64; - СК2 5 6 — режим “Таймер”, такт = такт системной син- хронизации / 256; - СК1024 — режим “Таймер”, такт = такт системной синхронизации / 1024; - TO_FALLING_EDGE — режим “Счетчик”, такт —- внешний на входе ТО, активный фронт сигнала — нис- падающий; - TO_RISING_EDGE -— режим “Счетчик”, такт — внеш- ний на входе ТО, активный фронт сигнала — нарас- тающий. Пример использования: timerO—SOurce(STOP); //Останов Т/СО timer0_stop Директива включения: Заголовок: Описание: Пример использования: ftinclude <avr/timer.h> void timerO_stop(void) Сброс счетного регистра таймера/счетчика Т/СО. timerO_stop(); //TCNTO = 0 timer0_start Директива включения: Заголовок: Описание: Пример использования: #include <avr/timer.h> void timerO_start(void) Начало счета с 1 для таймера/счетчика Т/СО. timerO start(); //TCNTO = 1
Функции и макроопределения компилятора WinAVR 333 timer_enable_int Директива включения: Заголовок: Описание: #include <avr/interrupt.h> void timer_enable_int(unsigned_char ints) Модифицирует содержимое регистра TIMSK. Значение, передаваемое в качестве параметра ints, зависит от типа микроконтроллера Пример использования: //Разрешение прерывания по переполнению Т/С1 timer_enable_int(_BV(TOIE1)); //Запрет всех прерываний timer enable int(O); Макроопределения для работы со сторожевым таймером wdt_di s able Директива включения: Определение: Описание: #include <avr/wdt.h> #define wdt_disable( ) _wdt_write(0) Отключает сторожевой таймер, если это возможно, по- средством сброса разряда WDE в регистре WDTCR. wdt_enablе Директива включения: Определение: #include <avr/wdt.h> #define wdt_enable(timeout) \ _wdt_write((timeout) | _BV(WDE)) Описание: Включает сторожевой таймер, настраивая его на сраба- тывание через время, заданное через timeout (уста- новка разрядов WDP0-WDP2). Возможные значения timeout: - wdto_15MS — срабатывание через 16 мс (Vcc = 5 В) или 47 мс (Vcc = 3 В); - wdto_30MS — срабатывание через 32 мс (Vcc = 5 В) или 94 мс (Vcc = 3 В); - WDTO_60MS — срабатывание через 64 мс (Vcc = 5 В) или 190 мс (Vcc = 3 В); - wdto_12 0MS — срабатывание через 128 мс (Vcc= 5 В) или 380 мс (Vcc = 3 В); - WDTO_250MS — срабатывание через 256 мс (Vcc= 5 В) или 750 мс (Vcc = 3 В); - WDTO_500MS — срабатывание через 512 мс (Vcc= 5 В) или 1,5 с (Vcc = 3 В); - WDTO 1S — срабатывание через 1 с (Vcc= 5 В) или 3 с (Vcc = 3 В); - WDTO_2S — срабатывание через 2,1 с (Vcc = 5 В) или 6 с (Vcc = 3 В).
334 Приложение Д. Библиотечные функции и макроопределения wdt_reset Директива включения: Определение: #include <avr/wdt.h> ttdefine wdt_reset( ) \ asm volatile ("wdr") Описание: Сброс сторожевого таймера. _wdt_write Директива включения: Определение: #include <avr/wdt.h> #define _wdt_write(value) \ asm volatile ( \ "in tmp_reg , SREG " "\n\t" \ "cli" "\n\t" \ "wdr" "\n\t" \ "out %0,%l" "\n\t" \ "out SREG , tmp_reg " "\n\t" \ "out %0,%2" \ : /* no outputs */ \ : "I" (_SFR_IO_ADDR(WDTCR)), \ "r" (0x18), /*_BV(WDCE)|_BV(WDE)*/ \ "r" ((unsigned char)(value)) \ : "rO" ) Описание: Инициализация регистра управления сторожевым тай- мером значением value. Функции для работы со случайными числами random Директива включения: Заголовок: Описание: ttinclude <stdlib.h> long int rand(void) Возвращает псевдослучайное число в диапазоне от 0 до значения RANDOM_MAX. Начальное число для генерации псевдослучайной последовательности задается с помо- щью функции srandom(). По умолчанию, это число равно 1. Пример использования: for (int i=l; i<=10; i++) { printf("Number%d = %d", i, random()); } srandom Директива включения: Заголовок: Описание: #include <stdlib.h> void srand(unsigned long int seed) Устанавливает начальное число для генерации псевдо- случайной последовательности Пример использования: for (int i=l; i<=10; i++) { srandom(i); printf("Number%d = %d", i, random()); }
Функции и макроопределения компилятора WinAVR 335 Функции для работы с памятью calloc Директива включения: Заголовок: Описание: #include <stdlib.h> void* calloc(size_t nele, size_t size) Размещает в памяти nele элементов размером size каждый и заполняет выделенную область нулями. Пример использования: calloc(2, 10); free Директива включения: Заголовок: Описание: #include <stdlib.h> void abort(void *ptr) Делает область памяти, на которую ссылается ptr, дос- тупной для размещения других объектов. Если ptr == NULL, то никаких действий не происходит. Пример использования: if (i<0) exit(0) ; malloc Директива включения: Заголовок: Описание: #include <stdlib.h> void* malloc(size_t size) Выделяет size байт памяти (нулями не заполняются). В случае неудачи возвращает NULL. Пример использования: calloc(2, 10); memccpy Директива включения: Заголовок: #include <string.h> void* memccpy(void *dest, const void *src, int val, size_t len) Описание: Копирует не более, чем len, байтов из области памяти, на которую указывает sre. в область памяти, на кото- рую указывает dest. При нахождении символа val ко- пирование прекращается. Функция возвращает указа- тель на следующий символ в dest после val или NULL, если символ val не был найден в первых len символах sre Пример использования: #include <stdio.h> #include <string.h> char src[] = "Source string"; char dest [8]; void main(void) { memccpy(&dest, &src, ' ', 7); printf("dest = %s", dest); //Результат: "dest = Source "
336 Приложение Д. Библиотечные функции и макроопределения Макроопределения для обработки прерываний сП Директива включения: Определение: Описание: #include <avr/interrupt.h> #define cli() asm volatile ("cli::") Запрещает все прерывания путем сброса флага общего разрешения прерываний. EMPTY_INTERRUPT Директива включения: Определение: Описание: #include <avr/signal.h> #define EMPTY_INTERRUPT(signame) Определяет функцию обработчика “пустого” прерыва- ния. signame — имя макроопределения для типа пре- рывания (см. главу 3). INTERRUPT Директива включения: Определение: Описание: #include <avr/signal.h> #define INTERRUPT(signame) Определяет функцию обработчика прерывания для слу- чая, когда разрешено общее прерывание (т.е, обработ- чик может быть прерван), signame — имя макроопре- деления для типа прерывания (см. главу 3). sei Директива включения: Определение: Описание: #include <avr/interrupt.h> #define sei() asm volatile ("sei::") Разрешает все прерывания путем установки флага об- щего разрешения прерываний. SIGNAL Директива включения: Определение: Описание: ttinclude <avr/signal.h> #define SIGNAL(signame) Определяет функцию обработчика прерывания для слу- чая, когда общее прерывание запрещено (т.е, обработ- чик не может быть прерван), signame — имя макрооп- ределения для типа прерывания (см. главу 3). Макроопределения для работы с разрядами регистров _BV Директива включения: Определение: Описание: #include <avr/io.h> #define _BV(bit) (l<<(bit)) Преобразует номер разряда в байтовое значение.
Функции и макроопределения компилятора WinAVR 337 bit_is_clear Директива включения: Определение: #include <avr/io.h> #define bit_is_clear(sfr, bit) \ (!(sfr & _BV(bit))) Описание: Проверяет содержит ли разряд bit в регистре sfr лог. 0. Если да, то макрос возвращает ненулевое значе- ние. Если указанный разряд содержит лог. 1, то воз- вращается 0 bit_is_set Директива включения: Определение: Описание: #include <avr/io.h> #define bit_is_set(sfг, bit) (sfr & _BV(bit)) Проверяет содержит ли разряд bit в регистре sfr лог. 1. Если да, то макрос возвращает ненулевое значе- ние. Если указанный разряд содержит лог. 0, то воз- вращается 0 loop_until_bit_is_clear Директива включения: Определение: ttinclude <avr/io.h> #define loop_until_bit_is_clear(sfг, bit) \ do {} while (bit_is_set(sfr, bit)) Описание: Ожидает до тех пор, пока разряд bit в регистре sfr содержит лог. 1 1оop_until_bi t_is_set Директива включения: Определение: ftinclude <avr/io.h> ftdefine loop_until_bit_is_set(sfr, bit) \ do {} while (bit_is_clear(sfr, bit)) Описание: Ожидает до тех пор, пока разряд bit в регистре sfr содержит лог. 0 Макроопределения для работы с памятью EEPROM eeprom_busy_wai t Директива включения: Определение: #include <avr/eeprom.h> #define eeprom_busy_wait ( ) \ do {} while (!eeprom_is_ready ()) Описание: Организовывает бесконечный цикл до тех пор, пока память EEPROM не освободится. eeprom_i s_ready Директива включения: Определение: ttinclude <avr/eeprom.h> #define eeprom_is_ready ( ) \ bit_is_clear(EECR, EEWE) Описание: 1, если память EEPROM готова к новой операции чте- ния/записи. В противном случае — 0. 22-6-767
338 Приложение Д. Библиотечные функции и макроопределения Функции для работы с памятью EEPROM eeprom_read block Директива включения: Заголовок: #include <avr/eeprom.h> void eeprom_read_block(void *buf, const void *addr, size_t n) Описание: Считывает блок из n байт по адресу EEPROM addr в буфер buf. Пример использования: #include <stdio.h> #inciude <avr/eeprom.h> char buf[20]; void main(void) { eeprom_read_block(buf, 0x01, 10); 1 eeprom_read_byte J Директива включения: Заголовок: Описание: Пример использования: ttinclude <avr/eeprom.h> uint8_t eeprom_read_byte(const uint8_t *addr) Считывает один байт по адресу EEPROM addr. #include <stdio.h> #include <avr/eeprom.h> unsigned char b; void main(void) { b = eeprom_read_byte(0x01); 1 eeprom_read word 1 Директива включения: Заголовок: Описание: #include <avr/eeprom.h> uintl6_t eeprom_read__word(const uintl6_t *addr) Считывает 16-тиразрядное слово по адресу EEPROM addr. Пример использования: #include <stdio.h> ttinclude <avr/eeprom.h> unsigned int w; void main(void) { w = eeprom_read_word(0x01) ; }
Функции и макроопределения компилятора WinAVR 339 eeprom_write_block Директива включения: #include <avr/eeprom. h> Заголовок: void eeprom_write_block(const void *buf, void *addr, size_t n) Описание: Записывает блок из п байт из буфера buf по адресу EEPROM addr. Пример использования: ttinclude <stdio.h> #include <avr/eeprom.h> char buf[20] = "0123456789"; void main(void) { eeprom_write_block(buf, 0x01, 5); } eeprom_write_byte Директива включения: ttinclude <avr/eeprom. h> Заголовок: void eeprom_write_byte (uint8_t *addr, uint8_t val) Описание: Записывает байт val по адресу EEPROM addr. Пример использования: #include <stdio.h> ftinclude <avr/eeprom.h>. unsigned char b=10; void main(void) { eeprom_write_byte(0x01, b) ; } eeprom_wri te_word Директива включения: #include <avr/eeprom.h> Заголовок: void eeprom_write_word(uintl6_t *addr, uintl6_t val) Описание: Записывает 16-тиразрядное слово val по адресу EEPROM addr. Пример использования: #include <stdio.h> ttinclude <avr/eeprom.h> unsigned int w=60000; void main(void) { eeprom_write_word(0x01, w) ; } 22*
340 Приложение Д. Библиотечные функции и макроопределения Функции и макроопределения компилятора CCS-PICC Математические макроопределения LN2 Директива включения: Определение: Описание: LN10 Директива включения: Определение: Описание: PI Директива включения: Определение: Описание: PI_D IV_BY_TWO Директива включения: Определение: Описание: Директива включения: Определение: Описание: SQRT2 Директива включения: Определение: Описание: TWSBYPT Директива включения: Определение: Описание: ttinclude <math.h> ttdefine LN2 0.6931471806 Натуральный логарифм от 2 ftinclude <math.h> #define LN10 2.30258509 Натуральный логарифм от 10 ttinclude <math.h> #define PI 3.141592654 Число л ttinclude <math.h> #define PI_DIV_BY_TWO 1.570796326794896 Число Tt/2 #include <stdlib.h> ttdefine RAND_MAX 32767 Наибольшее значение, которое может быть сгенериро- вано функцией rand (). #include <math.h> ttdefine SQRT2 1.41421356 Квадратный корень из 2 ftinclude <math.h> #define TWOBYPI 0. Число 2/л Функции для работы со строками atof Директива включения: #include <stdlib.h> Заголовок: float atof (char *str) Описание: Преобразует строку в вещественное представление.
Функции и макроопределения компилятора CCS-PICC 341 Пример использования: #include <16F877.h> #include <stdlib.h> #fuses HS, NOWDT ' fuse delay(clock=10000000) fuse rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7) char str[10]; float x; void main(void) { gets (str); x - atof(str); printf ( "x=%f", x); atoi32 Директива включения: Заголовок: Описание: #include <stdlib.h> int32 atoi32(char *str) Преобразует строку в целочисленное представление (32-хразрядное целое). Пример использования: ttinclude <16F877.h> #include <stdlib.h> #fuses HS, NOWDT fuse delay(clock=10000000) fuse rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7) char str [10]; int32 x; void main(void) { gets (str); x = atoi32(str); printf("x=%d", x); } strcspn Директива включения: Заголовок: Описание: #include <string.h> unsigned char strcspn(char *str, char *set) Возвращает индекс первого символа в строке str, ко- торый совпадает с символом в строке set. Если ни од- ного из символов str нет в set, возвращается длина str. Если первый символ в str присутствует в set, Пример использования: возвращается 0 ttinclude <16F877.h> #include <string.h> #fuses HS, NOWDT void main(void) 1234567890- 0 char set1 [ ] char set2 [ ]
342 Приложение Д, Библиотечные функции и макроопределения char str[] = "8(050)111-11-11"; char il; char 12; il = strcspn(str, setl); //il = 0 i2 = strcspn(str, set2); //12 = 1 while(1); } striстр Директива включения: Заголовок: Описание: #include <string.h> signed char stricmp(char *sl, char *s2) Сравнивает две строки без учета регистра символов и возвращает число <0, если sl< s2; 0, если sl=s2 или число > 0, если si > s2 Пример использования: #include <16F877.h> #include <string.h> #fuses HS, NOWDT #use delay(clock=10000000) (fuse rs232 (baud=9600, xmit=PIN_C6, rcv=PIN_C7 ) char sl[] = "STRING 1"; char s2[] = "String 2"; int res; void main(void) { res = stricmp(&sl, &s2); printf("res=%d", res); //Результат: "res=-l" 1 s trpbrk Директива включения: Заголовок: Описание: ttinclude <string.h> char* strpbrk(char *str, char *set) Просматривает строку str в поисках первого вхожде- ния некоторого символа из строки set. Если вхожде- ние найдено, возвращается указатель на соответствую- щий символ в строке str. В противном случае возвра- щается NULL Пример использования: #include <16F877.h> #include <string.h> #fuses HS, NOWDT #use delay(clock=10000000) (fuse rs232 (baud=9600, xmit=PIN_C6, rcv=PIN_C7) void main(void) { char strl[] = "April 18, 2006"; char set[] - char *p; p = strpbrk(strl, set); //", 2006"
Функции и макроопределения компилятора CCS-PICC 343 printf("The year is %s", p+2); //Результат: "The year is 2006" while(1) ; } strspn Директива включения: Заголовок: Описание: #include <string.h> unsigned char strspn(char *str, char *set) Возвращает индекс первого символа в строке str, ко- торый не соответствует какому-либо символу в строке set. Если все символы в строке str присутствуют в строке set, возвращается длина str. Если в строке str нет символов из строки set, возвращается 0. Пример использования: #include <16F877.h> #include <string.h> frfuses HS, NOWDT void main(void) { char setl[] = "1234567890"; char set2[] = ".-()"; char str [ ] = "8(050)111-11-11"; char il; char i2; il = strspn(str, setl); //il = 1 i2 = strspn(str, set2); //i2 = 0 while(1) ; } strtok Директива включения: Заголовок: Описание: #include <string.h> char* strtok(char *s, char const *delim) Просматривает строку s в поиске первого символа, не входящего в строку delim. При этом предполагается, что si состоит из последовательности подстрок, разде- ленных символами из строки delim. Первый вызов функции вернет указатель на первый символ- разделитель, после которого в строке будет вставлен символ “\0”. Последующие вызовы функции будут воз- вращать следующие подстроки со вставкой вместо раз- делителя символа “\0” до тех пор, пока не будут про- смотрены все разделители. После этого возвращается NULL. Пример использования: #include <16F877.h> #include <string.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,xmit=PIN_C6, rcv=PIN_C7)
344 Приложение Д. Библиотечные функции и макроопределения char s[] = " (050)111-1111"; char delim [] = "()-"; void main(void) { char area_code[4]; char *prefix; char *postfix; strcpy(area_code, strtok(s, delim); prefix = strtok(0, delim); postfix = strtok(0, delim); printf("Area code: %s\n\r", area_code); printf("Prefix: %s\n\r", prefix); printf("Postfix: %s\n\r", postfix); while(1); } Функции ввода/вывода assert Директива включения: #include <assert.h> #use rs232() Заголовок: Описание: void assert(condition) Проверяет на этапе выполнения программы любое ус- ловное выражение condition. Если condition дает в результате FALSE, то в стандартный поток stderr вы- дается сообщении об ошибке. Сообщение об ошибке включает в себя имя файла и номер строки, в которой была вызвана функция assert. Если в программу включить директиву препроцессора #define NODEBUG, то компилятор проигнорирует вы- зов функции assert (т.е. не сгенерирует соответствую- щий машинный код). Пример использования: #include <16F877.h> #include <assert.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,parity=N,xmit=PIN_C6, rcv=PIN_C7,bits=8) void main(void) { assert(getc() == 'A'); while(1); } fgetc Директива включения: Заголовок: #use rs232() char fgetc(stream)
Функции и макроопределения компилятора CCS-PICC 345 Описание: Ожидает приема символа из потока USART, заданного параметром stream. Имя этого параметра должно сов- падать с тем, которое задано директивой #use rs232. Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232 (baud=9600, parity=N, xmit==PIN_C6, rcv=PIN_C7,stream=OUT_STREAM) void main(void) { char c; while(1) { c = fgetc(OUT_STREAM); //Прием и эхом fputc(c, OUT_STREAM); //передача } 1 fgets J Директива включения: Заголовок: Описание: #use rs232() void fgets(char *str, stream) Ожидает приема строки из потока USART, заданного параметром stream. Имя этого параметра должно сов- падать с тем, которое задано директивой #use rs232. Строка считывается в буфер, на который указывает па- раметр str, до тех пор, пока не встретится символ воз- врата каретки. Приемный буфер должен иметь доста- точный размер, чтобы поместить строку и завершающий символ “\0”. Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,parity=N,xmit=PIN_C6, rcv=PIN_C7,stream=OUT_STREAM) void main(void) { char buf[20]; while (1) { fgets(buf, OUT_STREAM); //Прием и эхом fputs(buf, OUT_STREAM); //передача } fprintf } Директива включения: Заголовок: fuse rs232 () void fprintf(stream, char *fmt, [argl, arg2, ...])
346 Приложение Д. Библиотечные функции и макроопределения Описание: Выводит форматированную строку fmt в поток USART, заданный параметром stream (должен совпадать со зна- чением в директиве #use rs232), с учетом подстановки параметров из списка argl, arg2... (не обязательный). Правила форматирования строки вывода рассматрива- ются в главе 3. Пример использования: #include <16F877.h> #fuses HS, NOWDT (fuse delay(clock=10000000) (fuse rs232 (baud=9600, parity=N, xmit=PIN_C6, rcv=PIN_C7,stream=OUT_STREAM) void main(void) char c; fprintf(OUT_STREAM, "Dec: %Lu\n\r", 50); fprintf(OUT_STREAM, "Hex: %LX\n\r", 50); c = getchar(); fprintf(OUT_STREAM, "Char: %c\n\r", c) ; while (1) ; fputc Директива включения: Заголовок: Описание: #use rs232() void fputc(char C, stream) Выводит символ С в поток USART, заданный парамет- ром stream (должен совпадать со значением в директи- ве #use rs232). Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) Use rs232 (baud=9600, parity=N, xmit=PIN_C6, rcv=PIN_C7,stream=OUT_STREAM) void main(void) { char c; while (1) { c = fgetc(OUT_STREAM); //Прием и эхом fputc(c, OUT_STREAM); //передача } fputs } Директива включения: Заголовок: Описание: fuse rs232() void fputs(char *str, stream) Выводит строку str в поток USART, заданный пара- метром stream (должен совпадать со значением в ди- рективе #use rs232). Переданная строка автоматиче- ски дополняется символами “\п\г”.
Функции и макроопределения компилятора CCS-PICC 347 Пример использования: #include <16F877.h> #fuses HS, NOWDT (fuse delay(clock=10000000) #use rs232(baud=9600, parity=N,xmit=PIN_C6, rcv=PIN_C7,stream=OUT_STREAM) void main(void) { char buf[20]; while(1) { fgets(buf, OUT_STREAM); //Прием и эхом fputs(buf, OUT_STREAM); //передача } } getc Директива включения: Заголовок: Описание: #use rs232() char getc() Ожидает приема символа из потока USART по умолча- нию (задан последней директивой fuse rs2 32 перед вызовом функции). Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,parity=N,xmit=PIN_C6, rcv=PIN_C7,stream=OUT_STREAM) void main(void) char c; while (1) { c = getc(); //Прием из OUT_STREEAM и putc(c); //эхом передача обратно } } gets Директива включения: Заголовок: Описание: #use rs232() void gets(char *str) Ожидает приема строки из потока USART по умолча- нию (задан последней директивой #use rs232 перед вызовом функции). Строка считывается в буфер, на ко- торый указывает параметр str, до тех пор, пока не встретится символ возврата каретки. Приемный буфер должен иметь достаточный размер, чтобы поместить строку и завершающий символ “\0”.
348 Приложение Д. Библиотечные функции и макроопределения Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,parity=N,xmit=PIN_C6, rcv=PIN_C7,stream=OUT_STREAM) void main(void) { char buf[20]; while(1) { gets(buf); //Прием из OUT_STREAM и puts(buf); //эхом передача обратно } input J Директива включения: Заголовок: Описание: char input(const pin) Возвращает текущее состояние вывода pin. Используе- мый метод ввода-вывода зависит от последней директи- вы #use *_1О. Пример использования: ^include <16F877.h> #fuses HS, NOWDT #use fixed_io(d_outputs=PIN_D0) void main(void) { port_b_pullups(TRUE); while (1) { output_bit(PIN_D0, input(PIN_B0)); } input_X } Директива включения: Заголовок: char input_A() char input_B() char input_C() char input_D() char input_E() Описание: Пример использования: Считывает байт из порта А, В, С, D или Е. ftinclude <16F877.h> ttfuses HS, NOWDT #use fixed_io(d_outputs=PIN_D7,PIN_D6, PIN_D5,PIN_D4,PIN_D3,PIN_D2,PIN_D1,PIN_D0) void main(void) { port_b_pullups(TRUE);
Функции и макроопределения компилятора CCS-PICC 349 while(1) { output_d(input_b() ) ; } kbhit J Директива включения: Заголовок: Описание: #use rs232() char kbhit() Возвращает 0 (FALSE), если функции getc необходимо ожидать прихода символа, или 1 (TRUE), если символ готов. В случае программной реализации USART функ- ция возвращает TRUE, если на входной вывод поступил первый бит символа. Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600, parity=N,xmit=PIN_C6, rcv=PIN_C7) void main(void) { while(1) { if (kbhit ()) putc(getc()); //Эхо-передача символа } output_bit Директива включения: Заголовок: Описание: г void output_bit(const pin, char value) Выводит value (0 или 1) через вывод pin. Используе- мый метод ввода-вывода зависит от последней директи- вы #use *_Ю. Пример использования: ttinclude <16F877.h> #fuses HS, NOWDT #use fixed_io(d_outputs=PIN_D0) void main(void) { port_b_pullups(TRUE) ; while(1) { output Joit(PIN_D0, input(PIN_B0) ); } }
350 Приложение Д. Библиотечные функции и макроопределения output_f1оat Директива включения: Заголовок: Описание: void output_float(const pin) Переводит вывод pin в состояние входа. Используемый метод ввода-вывода зависит от последней директивы #use *_Ю. Пример использования: #include <16F877.h> #fuses HS, NOWDT fuse delay(clock=10000000) #use standard—io(D) #use fixed—io(c_outputs=PIN—C2) void main(void) { char b; while (1) { output_low(PIN_C2); delay_ms(500); output—float(PIN—DO); b = 0; while (b == 0) b = input(PIN_D0); output—high(PIN—C2); for (b=0; b<10; b++) { output—low(PIN—DO); delay_ms(500); output—high(PIN—DO); delay_ms(500); } } output_high Директива включения: Заголовок: Описание: } void output—high(const pin) Переводит вывод pin в состояние лог. 1. Используемый метод ввода-вывода зависит от последней директивы #use *_1О. Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use standard—io(D) #use fixed_io(c_outputs=PIN_C2) void main(void) { char b; while(1)
Функции и макроопределения компилятора CCS-PICC 351 { output-low(PIN_C2); delay_ms(500); output—float(PIN—DO); b = 0; while (b == 0) b — input(PIN_DO); output—high(PIN_C2); for (b=0; b<10; b++) { output—low(PIN—DO); delay—ms(500); output—high(PIN—DO); delay_ms(500); } } } output_low Директива включения: Заголовок: Описание: void output—low(const pin) Переводит вывод pin в состояние лог. 0. Используемый метод ввода-вывода зависит от последней директивы #use *_Ю. Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use standard—io(D) #use fixed—io(c_outputs=PIN_C2) void main(void) { char b; while(1) { output—low(PIN—C2); delay_ms(500); output—float(PIN—DO); b = 0; while (b == 0) b = input(PIN_DO); output—high(PIN_C2); for (b=0; b<10; b++) { output—low(PIN—DO); delay_ms(500); output—high(PIN_DO); delay_ms(500); } } }
352 Приложение Д. Библиотечные функции и макроопределения output_X Директива включения: Заголовок: void output_A(char value) void output_B(char value) void output_C(char value) void output_D(char value) void output_E(char value) Описание: Пример использования: Выводит байт value в порт А, В, C, D или Е. #include <16F877.h> #fuses HS, NOWDT fuse fixed_io(d_outputs=PIN_D7,PIN_D6, PIN_D5 , PIN_D4 , PIN_D3, PIN___D2 , PIN_D1, PIN_D0 ) void main(void) { port_b_pullups(TRUE); while(1) { output_d(input_b()); } i port_b_pullups Директива включения: Заголовок: Описание: ! void port_b_pullups(char value) Активизирует (value=TRUE) или отключает (value=FALSE) подтягивающие резисторы порта В. Пример использования: #include <16F877.h> #fuses HS, NOWDT fuse fixed_io(d_outputs=PIN_D7,PIN_D6, PIN__D5, PIN_D4, PIN_D3, PIN_D2 , PIN_D1, PIN_D0 ) void main(void) { port_b_pullups(TRUE) ; while(1) { output_d(input_b()); } i printf J Директива включения: Заголовок: Описание: fuse rs232() void printf(char *fmt, [argl, arg2, ...]) Выводит форматированную строку fmt в поток USART по умолчанию с учетом подстановки параметров из спи- ска argl, arg2... (не обязательный). Правила формати- рования строки вывода рассматриваются в главе 3.
Функции и макроопределения компилятора CCS-PICC 353 Пример использования: #include <16F877.h> ffuses HS, NOWDT fuse delay(clock=10000000) fuse rs232(baud=9600, parity=N, xmit=PIN_C6, rcv=PIN_C7,bits=8) void main(void) { char c; printf("Dec: %Lu\n\r", 50); printf("Hex: %LX\n\r”, 50); c = getchar(); printf("Char: %c\n\r", c) ; while(1) ; } putc Директива включения: Заголовок: Описание: Пример использования: #use rs232() void putc(char C) Выводит символ С в поток USART по умолчанию. #include <16F877.h> ffuses HS, NOWDT (fuse delay (clock=10000000) fuse rs232(baud=9600,parity=N,xmit=PIN_C6, rcv=PIN_C7,stream=OUT_STREAM) void main(void) { char c; while (1) { c = getc(); //Прием и эхом putc(c); //передача } } puts Директива включения: Заголовок: Описание: fuse rs232() void puts(char *str) Выводит строку str в поток USART по умолчанию. Пе- реданная строка автоматически дополняется символами “\п\г”. Пример использования: finclude <16F877.h> ffuses HS, NOWDT fuse delay(clock=10000000) fuse rs232(baud=9600, parity=N,xmit=PIN C6, rcv=PIN_C7,stream=OUT STREAM) void main(void) { 23 — 6-767
354 Приложение Д. Библиотечные функции и макроопределения char buf[20]; while(1) { gets(buf); //Прием и эхом puts(buf); //передача } set_tris_X J Директива включения: Заголовок: fuse fast_io void set_tris_A(char value) void set_tris_B(char value) void set_tris_C(char value) void set_tris_D(char value) void set_tris_E(char value) Описание: Задает направление передачи данных через порт соглас- но value. Если в некотором разряде value установлена лог. 1, то соответствующий вывод порта работает как вход, иначе — как выход. Пример использования: #include <16F877.h> #fuses HS, NOWDT fuse delay(clock=10000000) fuse fast_io(D) (fuse f ixed_io (c_outputs = PIN_C2) void main(void) { char d; while(1) { output_low(PIN_C2); delay_ms(5000); set_tris_D(OxFF); d = input_D(); output_high(PIN_C2); set_tris_d(0); output_D(dA0xFF); de1a y_ms(5000); } 1 set_uart_speed J Директива включения: Заголовок: Описание: (fuse rs232 () void set_uart_speed(const int32 baud) Изменяет скорость передачи данных через аппаратный последовательный порт RS232. baud — константа в диа- пазоне от 100 до 115200.
Функции и макроопределения компилятора CCS-PICC 355 Пример использования: ((include <16F877.h> #fuses HS, NOWDT (fuse delay (clock=10000000) (fuse rs232 (baud=9600, parity=N, xmit=PIN_C6, rcv=PIN_C7) (fuse fixed_io (b__outputs=PIN_B4 , PIN_B3, PIN_B2, PIN_B1,PIN_B0) void main(void) { port__b_pullups (TRUE) ; while(1) { switch(input_B()) //Выбор скорости UART { //с помощью переключателей порта В case 0x00: set_uart_speed(2400); break; case 0x01: set_uart_speed(4800); break; case 0x03: set_uart_speed(9600); break; case 0x07: set_uart_speed(19600); break; case OxOF: set_uart_speed(38400); break; case OxlF: set_uart_speed(57600); break; } delay_ms(1000) ; } sprintf ; Директива включения: Заголовок: void sprintf(char *str, char flash *fmt, [argl, arg2, ...]) Описание: Копирует форматированную строку fmt в строку str с учетом подстановки параметров из списка argl, arg2... (не обязательный). Правила форматирования строки вы- вода рассматриваются в главе 3. Строка-приемник должна быть достаточного размера для сохранения сформированной форматированной строки. Пример использования: ((include <16F877.h> ((fuses HS, NOWDT (fuse delay (clock=10000000) (fuse rs232 (baud=9600, parity=N, xmit=PIN_C6, rcv=PIN_C7,bits=8) char s [40]; void main(void) 23*
356 Приложение Д. Библиотечные функции и макроопределения sprintf(s, "Dec: %Lu\n\r", 50); while(1); Функции управления микроконтроллером de1ау_сусles Директива включения: Заголовок: Описание: Пример использования: void delay_cycles(const unsigned char C) Создает задержку, длина которой соответствует количе- ству С последовательностей из четырех тактов систем- ной синхронизации. Значение С может находиться в диапазоне от 1 до 255. Функция не использует таймеров, поэтому, в результате возникновения запроса на преры- вание в процессе ее работы, фактическая задержка мо- жет оказаться больше. #include <16F877.h> #fuses HS, NOWDT (fuse delay (clock=10000000) (fuse fixed_io (c_outputs = PIN_C2) void main(void) вывода 2 порта С с 500 мс С2, 0); int16 с; while(1) { //Переключение //интервалом в output_bit(PIN for (с=0; с<5000; C++) delay_cycles(250); output_bit(PIN—C2); delay_ms(500); delay ms Директива включения: Заголовок: Описание: delay delay_ms(const unsigned char C) delay_ms(const intl6 C) void void Создает задержку на С миллисекунд. Если С — перемен- ная, то ее значение может находиться в диапазоне от 1 до 255. Если С — константа, то ее значение может нахо- диться в диапазоне от 1 до 65535. Эта функция не ис- пользует таймеров, поэтому, в результате возникновения запроса на прерывание в процессе ее работы, фактиче- ская задержка может оказаться больше.
Функции и макроопределения компилятора CCS-PICC 357 Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use fixed_io(c_outputs = PIN_C2) void main(void) { int16 c; while(1) { //Переключение вывода 2 порта С с //интервалом в 500 мс output_bit(PIN_C2, 0); for (с=0; с<5000; с++) delay_cycles(250); output_bit (PIN__C2) ; delay_ms(500); } } delay_us Директива включения: Заголовок: #use delay void delay_us(const unsigned char 0) void delay_us(const int16 C) Описание: Создает задержку на С микросекунд. Если с — перемен- ная, то ее значение может находиться в диапазоне от 1 до 255. Если С — константа, то ее значение может нахо- диться в диапазоне от 1 до 65535. Эта функция не ис- пользует таймеров, поэтому, в результате возникновения запроса на прерывание в процессе ее работы, фактиче- ская задержка может оказаться больше. Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232 (baud=9600, parity=N, xmit=PIN__C6, rcv=PIN_C7,bits=8) #use fixed_io(c_outputs = PIN_C2) #use fixed_io(d_outputs = PIN_D0) void main(void) { long last_ccpl; setup_timer_l(T1_INTERNAL | T 1_DIV_JBY_8 ) ; setup_ccpl(CCP_CAPTURE_FE); last_ccpl = ccp_l; while(1) { output_bit(PIN_D0, input(PIN_C2)); delay_us(1000); if (last_ccpl != ccp_l {
358 Приложение Д. Библиотечные функции и макроопределения printf("%lu\n\r", сср_1); last_ccpl = сср_1; } } } disable_interrupts Директива включения: Заголовок: Описание: #int_xxx void disable_interrupts(int_level) Запрещает прерывание на уровне intlevel. int_level — это константа, представляющая вид пре- рывания (совпадает с именем директивы #int_xxx). Например, прерыванию при переполнении TMR0 соот- ветствует константа int_rtcc, а внешнему прерыва- нию— константа INT_EXT. Всем прерываниям соответ- ствует константа GLOBAL, Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,xmit=PIN_C6,rcv=PIN C7) byte secs; //Счетчик секунд byte с; //Количество прерываний, //оставшееся до истечения секунды #int_rtcc //Эта функция вызывается при //каждом переполнении TMR0, т.е. //примерно 38 раз в секунду void tmrO isr() { if(——с == 0) { ++secs; c = 38; } } void main(void) { c = 38; set_timerO(0) ; setup_counters(RTCC_INTERNAL, RTCC_DIV_256); enable_interrupts(GLOBAL); while(1) { getc(); //Ожидаем символ - начало secs = 0; enable_interrupts(INT_RTCC); getc(); //Ожидаем символ - конец disable_interrupts(INT_RTCC); printf("%u seconds", secs); } }
Функции и макроопределения компилятора CCS-PICC 359 enable interrupts Директива включения: Заголовок: Описание: #int_xxx void enable_interrupts(int_level) Разрешает прерывание на уровне intlevel. int level — это константа, представляющая вид пре- рывания (совпадает с именем директивы #int_xxx). Например, прерыванию при переполнении TMR0 соот- ветствует константа int rtcc, а внешнему прерыва- нию— константа int ext. Всем прерываниям соответ- ствует константа GLOBAL. Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232 (baud=9600,xmit=PIN_C6,rcv=PIN_C7) byte secs; //Счетчик секунд byte с; //Количество прерываний, //оставшееся до истечения секунды #int__rtcc //Эта функция вызывается при //каждом переполнении TMR0, т.е. //примерно 38 раз в секунду void tmrO_isr() { if(--с == 0) { ++secs; с = 38; } } void main(void) { c = 38; set__timerO (0) ; setup_counters(RTCC_INTERNAL, RTCC_DIV_256); enable_interrupts(GLOBAL); while(1) { getc(); //Ожидаем символ - начало secs = 0; enable_interrupts(INT_RTCC); getc(); //Ожидаем символ - конец disable_interrupts (INT__RTCC) ; printf("%u seconds", secs); } }
360 Приложение Д. Библиотечные функции и макроопределения ext_int_edge Директива включения: Заголовок: Описание: void ext_int_edge([source] , edge) Определяет, какой фронт сигнала на входе внешнего за- проса на прерывание вызывает прерывание. Если edge = L ТО_Н, то активным является нарастающий фронт, а значению H_TO_L соответствует ниспадающий фронт. Параметр source используется только в микроконтрол- лерах семейства PIC18, в которых можно задать источ- ник внешнего прерывания. Пример использования: #include <18F458.h> #use delay(clock=10000000) #fuses HS, NOWDT intl6 msecs; //Значение миллисекунд int secs; //Значение секунд unsigned char is_output; #int_timerl //Переполнение TMR1 void timerl_isr(void) { if (is_output) //Если вывод секунд,... { if (++msecs == 1000) { //Если миллисекунд 1000, то //увеличиваем значение секунды if (++secs == 60) secs = 0; msecs = 0; output_D(-secs); //Выводим в порт D //значение секунды } } } #int_ext //Внешнее прерывание по входу INTO void ext_isr(void) { delay_ms(100); if (is_output & 1) //Если вывод секунд, ext_int_edge(0, L_TO_H); else //Если вывод секунд не активен,... set_timerl(55536); msecs = 0; secs = 0; ext_int_edge(0, H_TO_L); } is__output Л= is_output; }
Функции и макроопределения компилятора CCS-PICC 361 void main() { port_b_pullups(TRUE); ext_int_edge(0, L_TO_H); is_output = 0; setup_timer_l(T1_INTERNAL | T1_DIV_BY_1); enable_interrupts(INT_EXT); enable interrupts(INT TIMER1); enable__interrupts (GLOBAL) ; while(1); //Ожидаем внешнего прерывания reset_cpu Директива включения: Заголовок: Описание: Пример использования: void reset__cpu() Сброс микроконтроллера. #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7); void main() { while(1) { if (getc() == 'R') reset_cpu(); } restart cause Директива включения: Заголовок: Описание: char restart_cause() Возвращает значение, указывающее на причину послед- него сброса процессора, например: WDT_FROM_SLEEP, WDTJTIMEOUT, MCLR_FROM_SLEEP, NORMAL_POWER_UP И др. Пример использования: #include <16F877.h> Ifuses HS, WDT #use delay(clock=10000000) #use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7); void main () { switch(restart_cause () ) { ’ case WDT_TIMEOUT: { puts("Restarted - Watchdog timeout"); break;
362 Приложение Д. Библиотечные функции и макроопределения } case NORMAL_POWER_UP: { puts("Restarted - Normal power up"); break; } } setup_wdt(WDT_2304MS); while(1) { restart_wdt(); puts ("Hit any key to avoid WD timeout"); getc(); } sleep Директива включения: Заголовок: void sleep () Описание: Выполняет команду sleep. Пример использования: #include <16F877.h> #fuses HS, WDT #use delay(clock=10000000) #use rs232(baud=9600,xmit=PIN_C6,rcv=PIN C7); char sleep_flag; #int_ext void ext_isr() { static char button_pressed = FALSE; if (1button_pressed) { button_pressed = TRUE; sleep_flag = TRUE; ext_int_edge(L_TO_H); } else { button_pressed = FALSE; sleep_flag = FALSE; ext_int_edge(H_TO_L); } if (!input(PIN_B0)) button_pressed = TRUE; delay_ms(100); void main()
Функции и макроопределения компилятора CCS-PICC 363 { sleep_flag = FALSE; port_b_pullups(TRUE); ext_int_edge (H_TO_L) ; enable_interrupts(INT_EXT); enable_interrupts(GLOBAL); while(1) { if (sleep_flag) sleep(); de1a y_ms(1000); } Функции для работы с таймерами и модулем ССР get_rtcc Директива включения: Заголовок: Для микроконтроллеров с 8-миразрядным TMR0: int8 get_rtcc() Для микроконтроллеров с 16-тиразрядным TMR0: intl6 get_rtcc() Описание: Пример использования: Возвращает текущее значение счетного регистра TMR0. #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600, parity=N,xmit=PIN_C6, rcv=PIN_C7,bits=8) void main(void) { setup_timer_0(RTCC_INTERNAL|RTCC_DIV_8); set_rtcc(0); //Сбрасываем TMRO printf("This is just for time\n\r"); printf("Timer ticks: %ld", get_rtcc()); while(1); } get_timerX Директива включения: Заголовок: Для микроконтроллеров с 8-миразрядным TMRO: int8 get_timerO() Для микроконтроллеров с 16-тиразрядным TMRO: intl6 get_timerO() Для микроконтроллеров с TMR1, TMR2 и TMR3: inti6 get—timerl() int8 get—timer2() intl6 get—timer3() Описание: Возвращает текущее значение счетного регистра соот- ветствующего таймера/счетчика.
364 Приложение Д. Библиотечные функции и макроопределения Пример использования: restart_wdt Директива включения: Заголовок: Описание: Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,parity=N,xmit=PIN_C6, rcv=PIN_C7,bit s=8) void main(void) { setup_timer_l(T1_INTERNAL|T1_DIV_BY_8); set_timerl(0); //Сбрасываем TMR1 printf("This is just for time\n\r"); printf("Timer ticks: %ld", get_timerl()); while(1) ; Sfuses WDT void restart_cause() Сбрасывает сторожевой таймер. #include <16F877.h> #fuses HS, WDT #use delay(clock=10000000) #use rs232 (baud=9600, xmit=PIN_C6, rcv=PIN__C7) void main() { switch(restart_cause()) { case WDT_TIMEOUT: { puts("Restarted - Watchdog timeout”); break; } case NORMAL_POWER_UP: { puts("Restarted - Normal power up"); break; } } setup_wdt (WDT__2304MS) ; while (1) { restart_wdt(); puts("Hit any key to avoid WD timeout"); getc(); }
Функции и макроопределения компилятора CCS-PICC 365 set pwmX duty Директива включения: Заголовок: void set_pwml_duty(intl6 value) void set_pwm2_duty(int16 value) Описание: Записывает в модуль ССР 10-тиразрядное значение value для установки рабочего цикла ШИМ. Пример использования: #include <18F458.h> #use delay(clock=10000000) #fuses HS, NOWDT #use fixed_io(c_outputs = PIN_C2) void main() { int16 duty; setup_timer__2 (T2_DIV_BY_16, 250, 1); setup_ccpl (CCP__PWM) ; duty = 0; while(1) { set_pwml_duty(duty); duty += 100; duty &= 0x3FF; //Только 10 разрядов delay__ms (500) ; } set_rtcc Директива включения: Заголовок: } Для микроконтроллеров с 8-миразрядным TMR0: void set_rtcc(int8 value) Для микроконтроллеров с 16-тиразрядным TMR0: void set_rtcc(intl6 value) Описание: Пример использования: Устанавливает значение счетного регистра TMR0. #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600, parity=N,xmit=PIN_C6, rcv=PIN_C7, bits=8) void main(void) { int16 val; setup__rtcc (RTCC_INTERNAL | RTCC_DIV_8 ) ; set_rtcc(0); //Сбрасываем TMR0 printf("This is just for time\n\r"); printf("Timer ticks: %ld", get_rtcc()); while(1); }
366 Приложение Д. Библиотечные функции и макроопределения set_timerX Директива включения: Заголовок: Для микроконтроллеров с 8-миразрядным TMRO: void set_timerO(int8 value) Для микроконтроллеров с 16-тиразрядным TMRO: void set_timerO(intl6 value) Для микроконтроллеров c TMR1, TMR2 и TMR3: void set_timerl(intl6 value) void set_timer2(int8 value) voie set_timer3(intl6 value) Описание: Устанавливает значение счетного регистра соответст- вующего таймера/счетчика. Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,parity=N,xmit=PIN_C6, rcv=PIN_C7,bits=8) void main(void) { setup_timer_l (T1_INTERNAL | T1__DIV_BY_8 ) ; set_timerl(0); //Сбрасываем TMRl printf("This is just for time\n\r"); printf("Timer ticks: %ld", get_timerl()); while(1) ; } setup_ccpX Директива включения: Заголовок: void setup_ccpl(const mode) void setup_ccp2(const mode) Описание: Инициализирует ССР в соответствии со значением константы mode (к счетчикам ССР можно получить доступ с помощью глобальных переменных сср_1 и сср_2.): - CCP OFF — модуль ССР отключен; - ccp_capture_fe — захват состояния TMR1 по нис- падающему фронту на выводе ССРх; - ccp_capture_re — захват состояния TMR1 по на- растающему фронту на выводе ССРх; - CCP_CAPTURE_DIV_4 — захват состояния TMR1 по каждому четвертому нарастающему фронту на выводе ССРх; - CCP_CAPTURE_div_1 6 — захват состояния TMR1 по каждому шестнадцатому нарастающему фронту на вы- воде ССРх; - ccp_compare_set_on_match — установка лог. 1 на выводе ССРх при совпадении содержимого TMR1 и переменной сср_х;
Функции и макроопределения компилятора CCS-PICC 367 Пример использования: s е tup__t ime r_X Директива включения: Заголовок: Описание: - CCP_COMPARE_CLEAR_ON_MATCH — установка ЛОГ. О на выводе ССРх при совпадении содержимого TMR1 и переменной сср_х; - ccp_compare_int — формирование запроса на пре- рывание при совпадении содержимого TMR1 и пере- менной сср_х; - ccp_compare_RESET_timer — сброс таймера при совпадении содержимого TMR1 и переменной сср_х; - ccp pwm — модуль ССР работает в режиме ШИМ. #include <16F877.h> #fuses HS, NOWDT fuse delay(clock=10000000) fuse rs232(baud=9600,parity=N,xmit=PIN_C6, rcv=PIN_C7,bits=8) fuse fixed_io(c_outputs = PIN_C2) fuse fixed_io(d_outputs = PINEDO) void main(void) { long last_ccpl; setup__timer_l (T1_INTERNAL f T1_DIV_BY_8 ) ; setup_ccpl(CCP_CAPTURE_FE); last_ccpl = ccp_l; while (1) { output_bit(PIN—DO, input(PIN_C2)); delay_us(1000); if (last_ccpl != ccp_l { printf("%lu\n\r", ccp_l); last—Ccpl = ccp_l; } } void setup__timer_0 (mode) void setup__timer_l (mode) void setup_timer_2(mode, unsigned char period, int8 postscale) void setup—timer_3(mode) Инициализация соответствующего таймера/счетчика. Параметр mode — константа, состоящая из одной или нескольких опций. Параметр period (от 0 до 255) опре- деляющее момент сброса значения счетчика. Параметр post scale (от 1 до 16) определяет, сколько сбросов таймера должно произойти перед возникновением пре- рывания. Возможные значения для параметра mode:
368 Приложение Д. Библиотечные функции и макроопределения Тай- мер Источник такто- вых сигналов Преддели- тель частоты Другие 0 RTCC—INTERNAL RTCC—EXT—L_TO—H RTCC—EXT—H_TO—L RTCC—DIV—2 RTCC—DIV—4 RTCC—DIV—8 RTCC—DIV—16 RTCC—DIV_32 RTCC—DIV—64 RTCC—DIV—128 RTCC DIV 256 RTCC—OFF RTCC—8—BIT 1 Tl_DISABLED Tl—INTERNAL Tl—EXTERNAL Tl EXTERNAL SYNC rH <\1 ’O’ CO 1 1 1 m m m m 1 1 I > > > > НИНЫ Q Q Q Q 1 1 1 Ен Ь Ь E-h Tl—CLK—OUT 2 T2—DISABLED T2—DIV—BY—1 T2_DIV—BY—4 T2 DIV BY 16 3 T3_DISABLED T3_INTERNAL T3—EXTERNAL T3 EXTERNAL SYNC H H H H w о и с и ИННЫ < < < < 1 1 1 (Л DO СО DO к к к: 1 1 1 со М М Пример использования: #include <16F877.h> #fuses HS, WDT #use delay(clock=10000000) int cO, cl, c2; #int_timerO void timerO_isr(void) { if (c0 + + & 8) output_bit(PIN_D0, 1); else output—bit(PIN—DO, 0); } #int_timerl void timerl—isr(void) { if (cl++ & 8) output_bit(PIN-Dl, 1); else output—bit(PIN DI, 0); } #int_timer2 void timer2_isr(void) { if (c2++ & 8) output_bit(PIN_D2, 1); else output—bit(PIN—D2, 0);
Функции и макроопределения компилятора CCS-PICC 369 void main() { . setup_timer_l (RTCC_INTERNAL, RTCC__DIV__2 ) ; setup_timer_l(T1_INTERNAL | T1_DIV_BY_4); setup_timer_2 (T2___DIV_BY__1 6, 255, 1); enable_interrupts (int__timerO ) ; enable_interrupts(int_timerl); enable_interrupts(int_timer2); enable_interrupts(global); wh i1e (1) ; } setup_wdt Директива включения: Заголовок: Описание: Пример использования: ttfuses WDT void setup_WDT(mode) Инициализация сторожевого таймера. Параметр mode — константа, состоящая из одной или нескольких опций, определяющих время до наступления сброса от сторожевого таймера: wdt_18ms, wdt_3 6ms, wdt_7 2ms, WDTJ44MS, WDT-288MS, WDT_57 6MS, WDT_1152MS, WDT_2304MS. #include <16F877.h> #fuses HS, WDT #use delay(clock=10000000) #use rs232(baud=9600, xmit=PIN_C6,rcv=PIN_C7) ; void main () { switch (restart__cause () ) { case WDT_TIMEOUT: { puts("Restarted - Watchdog timeout"); break; } case NORMAL_POWER_UP: { puts("Restarted - Normal power up"); break; } } setup_wdt(WDT_2304MS); while(1) { restart_wdt(); puts("Hit any key to avoid WD timeout"); getc ( ) ; } 24 — 6-767
370 Приложение Д. Библиотечные функции и макроопределения Функции для работы с разрядами и памятью bit_clear Директива включения: Заголовок: void bit_clear(int8 var, char bit); void bit_clear(intl6 var, char bit); void bit_clear(int32 var, char bit); Описание: Очищает разряд bit целочисленной переменной var. В случае, если var имеет тип int8, bit может прини- мать значения от 0 до 7; для int 16 — от 0 до 15, а для int 32 —отО ДО 31. Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,xmit=PIN_C6, rcv=PIN_C7) void main(void) { int8 a; a = OxF; printf("a=0x%X\n\r", a); //a=0xF bit_clear(a, 3); printf("a=0x%X\n\r", a); //a=0x7 while(1); } bit set Директива включения: Заголовок: void bit_set(int8 var, char bit); void bit_set(intl6 var, char bit); void bit_set(int32 var, char bit); Описание: Устанавливает в лог. 1 разряд bit целочисленной пере- менной var. В случае, если var имеет тип int8, bit может принимать значения от 0 до 7; для int 16 — от 0 до 15, адля int32 —отО до31. Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,xmit=PIN_C6, rcv=PIN C7) void main(void) { int8 a; a = 0x7; printf("a=0x%X\n\r", a); //a=0x7 bit_set(a, 3); printf("a=0x%X\n\r" , a); //a=0xF while(1);
Функции и макроопределения компилятора CCS-PICC 371 bit test Директива включения: Заголовок: inti bit_test(int8 var, char bit); inti bit_test(intlS var, char bit); inti bit—test(int32 var, char bit); Описание: Возвращает состояние разряда bit целочисленной пе- ременной var. В случае, если var имеет тип int8, bit может принимать значения от 0 до 7; для int 16 — от 0 до 15, а для int 32 — от 0 до 31. Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,xmit=PIN_C6, rcv=PIN_C7) void main(void) { char c; c = getc(); //Получаем символ от USART for (int i=0; i<8; i++) printf("Bit%d=%d\n\r", i, bit—test(c, i)); } while(1); } input Директива включения: Заголовок: Описание: char input(const pin) Возвращает текущее состояние вывода pin (адрес- константа вывода порта): 0 или 1. Используемый метод ввода/вывода зависит от последней директивы #use *_io перед вызовом функции. Пример использования: #include <16F877.h> ’ #fuses HS, NOWDT #use fixed—io(d_outputs=PIN DO) void main(void) { port—b—pullups(TRUE); while(1) { if (input(PIN—BO)) output_high(PIN_D0); else output—low(PIN—DO); } 24'
372 Приложение Д. Библиотечные функции и макроопределения makel6 Директива включения: Заголовок: Описание: intl6 makel6(int8 var_high, int8 varjow) Возвращает 16-тиразрядное значение, составленное из двух 8-миразрядных величин var_high (старший байт) и var_low (младший байт). Если разрядность одного из параметров больше 8 бит, используются только младшие 8 разрядов. Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000 ) int8 a; int16 b; int32 c; void main(void) { c = OxOlACOOlF; a = 0x12; b = makel6(a, c); //b = 0xl21F while(1) ; } make32 Директива включения: Заголовок: int32 make32(int8 varl [, int8 var2, int8 var3, int8 var4]) Описание: Возвращает 32-хразрядное значение, составленное из одного, двух, трех или четырех 8-миразрядных величин. Параметр varl соответствует самому старшему байту, а var4 — самому младшему. Если общее число разрядов меньше 32, то старшие разряды в возвращенном значе- нии заполняются нулями. Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) int8 a; int16 b; int32 c; void main(void) { a = 0x12; b = OxABCD; c = make32(a, b); //b = 0x0012ABCD while(1);
Функции и макроопределения компилятора CCS-PICC 373 make 8 Директива включения: Заголовок: int8 make8(intl6 var, int8 offset); int8 make8(int32 var, int8 offset); Описание: Возвращает 8-миразрядное значение, полученное из 16- ти или 32-хразрядной величины var путем смещения на offset байтов. Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) int8 a; int32 b; void main(void) { c = OxOlACOOlF; a = make8(c, 2); //a = OxAC; while (1) ; } offsetof Директива включения: Заголовок: Описание: #include <stddef.h> int8 offsetof(structure_type, field) Возвращает смещение (в байтах) поля field в структу- ре типа structure_type. Пример использования: #include <16F877.h> #include <stddef.h> ffuses HS, NOWDT struct sTime { int hour, min, sec; } myTime; void main(void) { int sec_offset, secs; //Инициализируем структуру нулями в полях memset(&myTime, 0, sizeof(myTime)); //Находим смещение поля sec sec_offset = offsetof(sTime, sec); //Назначаем значение полю sec *(&myTime + sec_offset) = 10; secs = myTime.sec; //secs = 10 while (1); 1
374 Приложение Д. Библиотечные функции и макроопределения offsetofbit Директива включения: Заголовок: Описание: #include <stddef.h> int8 offsetofbit(structure_type, field) Возвращает смещение (в битах) поля field в структуре типа structure_type. Пример использования: #include <16F877.h> finclude <stddef.h> fffuses HS, NOWDT struct some_struct { int fl, f2; inti f3; int f4 : 7; } myStruct; void main(void) { int f 4_of f set_in__bits, a; //Инициализируем структуру нулями в полях memset(&myTime, 0, sizeof(myTime)); //Находим смещение поля f4 f4_offset_in_bits = offsetofbit(some_struct, f4); //Назначаем значение полю f4 f4 = 3; //Считываем байт, содержащий поле f4 а = * (&myStruct+ ( f 4_of f set__in_bit s / 8 ) ) ; //Сдвигаем разряды к младшему разряду а >>= (f4_offset_in_bits % 8); //a = 3 while(1) ; } read_bank Директива включения: Заголовок: Описание: int8 read_bank(int8 bank, intl6 offset) Считывает байт данных из области RAM банка bank (1- 3, в зависимости от микроконтроллера) по смещению offset. Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600, parity=N,xmit=PIN C6, rcv=PIN_C7,bits=8) void main(void) { int8 i;
Функции и макроопределения компилятора CCS-PICC 375 rotate_left Директива включения: Заголовок: Описание: Пример использования: rotate_right unsigned char с; while(1) { i = 0; do //Запись в банк 1 принятых символов, { //пока не встретится '\г' с = getс (); write_bank(1, i++, с) ; } while ((с != 13) && (i < 20)); printf("\n\r"); i = 0; do //Считываем данные из банка 1 и { //передаем их через UART с = read__bank (1, i + +); putc(с) } while ((с != 13) && (i < 20)); } } void rotate_left(char *addr, char bytes) Выполняет циклический сдвиг влево на 1 разряд bytes байтов в области памяти, на которую ссылается addr. #include <16F877.h> ttfuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,xmit=PIN_C6, rcv=PIN C7) void main(void) { char a[4] = {0x02, 0x10, 0x22, 0x16}; int8 i; i = 1; while(1) { printf("i=%d: %X %X %X %X\n\r", i, a[3], a [ 2} , a [ 1], a[0]); rotate_left(a, 4); if (++i == 32) i = 1; } Директива включения: Заголовок: Описание: void rotate_pright (char *addr, char bytes) Выполняет циклический сдвиг вправо на 1 разряд bytes байтов в области памяти, на которую ссылается addr.
376 Приложение Д. Библиотечные функции и макроопределения Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,xmit=PIN_C6, rcv=PIN_C7) void main(void) { char a[4] = {0x02, 0x10, 0x22, 0x16}; int8 i; i = 1; while(1) { printf("i=%d: %X %X %X %X\n\r", i, a[3], a[2], a[l], a[0]); rotate_right(a, 4); if (++i == 32) i = 1; } shift left Директива включения: Заголовок: inti shift_left(char *addr, char bytes, inti value) Описание: Выполняет сдвиг влево на 1 разряд bytes байтов в об- ласти памяти, на которую ссылается addr. Первый раз- ряд заполняется значением value. Функция возвращает Пример использования: сдвинутый разряд (последний перед сдвигом). #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,xmit=PIN_C6, rcv=PIN C7) void main(void) { char a[4] = {0x02, 0x10, 0x22, 0x16}; int8 i; i = 1; while(1) { printf("i=%d: %X %X %X %X\n\r", i, a[3] , a[2] , a[1] , a[0] ) ; shift_left(a, 4, 0); if (++i == 32) i = 1; } shift_right } Директива включения: Заголовок: inti shift_right(char *addr, char bytes, inti value)
Функции и макроопределения компилятора CCS-PICC 377 Описание: Выполняет сдвиг вправо на I разряд bytes байтов в области памяти, на которую ссылается addr. Послед- ний разряд заполняется значением value. Функция воз- вращает сдвинутый разряд (первый перед сдвигом). Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay (dock=10000000 ) #use rs232 (baud=9600, xmit=PIN_C6, rcv=PIN__C7) void main(void) { char a[4] = {0x02, 0x10, 0x22, 0x16}; int8 i; i = 1; while(1) { printf("i=%d: %X %X %X %X\n\r", i, a[3] , a[2] , a[1] , a[0] ) ; shift^right(a, 4, 1); if (++i == 32) i = 1; } } swap Директива включения: Заголовок: Описание: Пример использования: void swap(int8 х) Меняет местами старший и младший полубайты х. #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,xmit=PIN_C6, rcv=PIN_C7) void main(void) { int8 x; x = OxAF; printf("Source x = %02X\n\r", x); swap(x); printf("Swapped x = %02X\n\r", x) ; while(1); } write bank Директива включения: Заголовок: void write_bank(int8 bank, intl6 offset, int8 value) Описание: Записывает байт данных value в область RAM банка bank (1-3, в зависимости от микроконтроллера) по сме- щению offset.
378 Приложение Д. Библиотечные функции и макроопределения Пример использования: #include <16F877.h> #fuses HS, NOWDT fuse delay(clock=10000000) #use rs232(baud=9600, parity=N,xmit=PIN _C6, rcv=PIN_C7,bits=8) void main(void) { int8 i; unsigned char c; while(1) { i = 0; do //Запись в банк 1 принятых символов, { //пока не встретится '\г' с = getс (); write__bank (1, i + +, с); } while ((с != 13) && (i < 20)); printf("\n\r"); i = 0; do //Считываем данные из банка 1 и { //передаем их через UART с = read_bank(l, i++); putc(с) } while ((с != 13) && (i < 20)); } } Функции для работы с памятью EEPROM read_eeprom Директива включения: Заголовок: Описание: Пример использования: int8 read_eeprom(int8 addr) Считывает байт данных из памяти EEPROM по адресу addr. #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,xmit=PIN_C6, rcv=PIN C7) void main(void) { printf("Last command: %c\n\r", read_eeprom(OxOA) ) ; while (1) { write_eeprom(OxOA, getc()); printf("Last command: %c\n\r", read_eeprom(OxOA)); } }
Функции и макроопределения компилятора CCS-PICC 379 write eeprom Директива включения: Заголовок: Описание: void write_eeprom(int8 addr, int8 value) Записывает байт данных value в памяти EEPROM по адресу addr. Пример использования: finclude <16F877.h> ffuses HS, NOWDT fuse delay(clock=10000000) fuse rs232(baud=9600,xmit=PIN_C6, rcv=PIN_C7) void main(void) { printf("Last command: %c\n\r”, read_eeprom(OxOA)); while(1) { write_eeprom(OxOA, getc()); printf("Last command: %c\n\r", read eeprom(OxOA)); } } read_program_eeprom Директива включения: Заголовок: Для 14-тиразрядных микроконтроллеров: int16 read_program_eeprom(int16 addr); Для 16-тиразрядных микроконтроллеров: int8 read_program_eeprom(int32 addr); Описание: Пример использования: Считывает данные из памяти программ по адресу addr. finclude <16F877.h> ffuses HS, NOWDT fuse delay(clock=10000000) fuse rs232(baud=9600,xmit=PIN_C6, rcv=PIN C7) fdefine ID_LOCATION OxlcOO fdefine ID_LENGTH 10 fdefine TOTAL_ID_LENGTH 16 fdefine ID_OVERHEAD 10 forg ID_LOCATION, ID_LOCATION + TOTAL_ID_LENGTH char const id[ID_LENGTH] = {"123456789"}; void main(void) { int8 temp8, i; int16 tempi6, addr; char c; printf("Press R to read, W to write\n\r"); while (1) { c = getc(); if (c == 'W') //Записываем новый ID
380 Приложение Д. Библиотечные функции и макроопределения { for (i=0; i<(ID_ LENGTH-1); i++) { temp8 = getc (); templ6 = temp8 + 0x3400; addr = ID__LOCATION+I + ID_OVERHEAD; write program eeprom(addr, tempi 6); } addr = ID_LOCATION+i+ID_OVERHEAD; write_program_eeprom(addr, 0x3400) ; } else if (c == 'R') //Считываем ID { for (i=0; i<(ID_LENGTH-1); i++) { addr = ID_LOCATION+i + ID__OVERHEAD; templ6 = read_program_eeprom(addr); id[i] = (int8)(templ6 & OxFF); } id[i] = '\0'; printf("ID: %s\n\r", id) ; } else putc('?'); //Неверная команда } } wri te_program_eeprom Директива включения: Заголовок: Для 14-тиразрядных микроконтроллеров: void write_program_eeprom(int16 addr, int16 data); Для 16-тиразрядных микроконтроллеров: void write_program_eeprom(int32 addr, int8 data); Описание: Записывает данные data в память программ по адресу addr. Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,xmit=PIN_C6, rcv=PIN_C7) #define ID_LOCATION OxlcOO #define ID_LENGTH 10 #define TOTAL_ID_LENGTH 16 #define ID OVERHEAD 10 iiorg ID_ /LOCATION, ID_LOCATION + TOTAL_ID_LENGTH char const id[ID_LENGTH] = {"123456789"}; void main(void) { int8 temp8, i;
Функции и макроопределения компилятора CCS-PICC 381 int16 tempi6, addr; char c; printf("Press R to read, W to write\n\r"); while(1) { c = getc(); if (c == 'W) //Записываем новый ID { for (i=0; i<(ID_ LENGTH-1); i++) { temp8 = getc(); temp16 = temp8 + 0x3400; addr = ID_LOCATION+I+ID_OVERHEAD; writejorograngeeprom(addr, templ6); } addr = ID_LOCATION+i+ID_OVERHEAD; write_program_eeprom(addr, 0x3400); } else if (c == 'R') //Считываем ID { for (i=0; i< (ID__LENGTH-1) ; i + + ) { addr = ID_LOCATION+i+IDjDVERHEAD; tempi6 = read_program_eeprom(addr) ; id[i] = (int8)(templ6 & OxFF); } id[i] = ’\0’; printf("ID: %s\n\r", id) ; } else putc('?'); //Неверная команда Функции для работы с интерфейсом SPI setup_spi Директива включения: Заголовок: Описание: void setup_spi(const modes) Инициализирует интерфейс SPI в соответствии с кон- стантой modes, задающей режим и скорость обмена данными. Возможна комбинация значений из следую- щих категорий: - выбор режима — SPI_SS_DISABLED, SPI_MASTER, SPI_SLAVE; - активный фронт синхроимпульса — SPI_L TO_H, spi_h_to__l; частота тактирования — SPI_CLK_DIV_4, SPI_CLK_DIV_16, SPI_CLK_DIV_64, SPI_CLK T2.
382 Приложение Д. Библиотечные функции и макроопределения Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,parity=N,xmit=PIN_C6, rcv=PIN_C7,bits=8) #use standard_io(D) void main(void) { unsigned char c; setup_spi(SPI_MASTER|SPI_L_TO_H| SPI_CLK_DIV_16); while(1) { c = getc(); //Считываем символ output_D(OxFF Л с); //Индикация spi_write(с); //Записываем с в SPI } 1 spi_da ta_i s_in J Директива включения: Заголовок: Описание: Пример использования: char spi_data_is_in() Возвращает TRUE, если в порт SPI приняты данные. #include <16F877.h> frfuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,parity=N,xmit=PIN_C6, rcv=PIN_C7,bits=8) #use standard_io(D) void main(void) { unsigned char c; setup_spi(SPI_SLAVE|SPI_L TO H| SPI_CLK_DIV_16); while(1) { if (spi_data_is_in()) c = spi_read(); //Считываем символ output_D(OxFF Л с); //Индикация spi_write(с); //Записываем с в SPI } } spi_read Директива включения: Заголовок: Описание: char spi_read() Возвращает значение, считанное из порта SPI.
Функции и макроопределения компилятора CCS-PICC 383 Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,parity=N,xmit=PIN_C6, rcv=PIN_C7,bits=8) fuse standard_io(D) void main(void) { unsigned char c; setup_spi (SPI__SLAVE | SPI__L_TO_H | SPI_CLK_DIV_16); while(1) { if (spi_data_is_in()) c = spi_read(); //Считываем символ output_D(OxFF Л с); //Индикация spi_write(с); //Записываем с в SPI } spi_write Директива включения: Заголовок: Описание: void setup_spi(char с) Устанавливает очередной байт данных для передачи в порт SPI. Если устройство — ведущее, то функция гене- рирует тактовый сигнал и передает байт. Если устройст- во ведомое, то байт не будет передан до тех пор, пока не будет получен тактовый сигнал от ведущего устройства. Пример использования: #include <16F877.h> ffuses HS, NOWDT fuse delay(clock=10000000) #use rs232(baud=9600, parity=N, xmit=PIN_C6, rcv=PIN_C7,bits=8) fuse standard_io(D) void main(void) { unsigned char c; setup_spi (SPI_MASTER| SPI__L_TO_H | SPI_CLK_DIV_16); while(1) { c = getc(); //Считываем символ output_D(OxFF A с); //Индикация spi_write(с); //Записываем с в SPI }
384 Приложение Д. Библиотечные функции и макроопределения Функции для работы с интерфейсом PSP psp_input_full Директива включения: Заголовок: Описание: inti psp_input_full() Возвращает 0, если из порта PSP не считывается ника- ких данных, или 1, если есть данные на чтение. Данные можно считывать с помощью глобальной переменной psp_data. Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,parity=N,xmit=PIN_C6, rcv=PIN__C7, bits=8 ) void main(void) { setup_psp(PSP_ENABLED); while(1) { if (psp_input_full()) printf ("PSP data: %X", psp ydata); } psp_output_full ) Директива включения: Заголовок: Описание: inti psp_output_full() Возвращает 0, если в порта PSP еще не было записано никаких данных, или 1, если данные уже были записаны. Данные можно записывать в PSP с помощью глобальной переменной psp_data. Пример использования: #include <16F877.h> #fuses HS, NOWDT (fuse delay(clock=10000000) (fuse rs232 (baud=9600, parity=N, xmit=PIN 06, rcv=PIN_C7, bits=8) void main(void) { unsigned char c; setup_psp(PSP_ENABLED); while(1) { c = getc(); //Считываем символ while (!psp_output_full()) psp_data = с; //Выводим с через PSP } }
Функции и макроопределения компилятора CCS-PICC 385 psp overflow Директива включения: Заголовок: Описание: inti psp_overflow() Возвращает 0, если порта PSP не находится в состоянии переполнения (данные записываются внешним устрой- ством до того как принятые прежде данные были прочи- таны микроконтроллером), или 1 в противном случае Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,parity=N,xmit=PIN_C6, rcv=PIN_C7,bits=8) void main(void) { setup_psp(PSP_ENABLED); while (1) { if (psp_input_full ()) if (psp_overflow()) printf("PSP is overflowed!"); else printf("PSP data: %X", psp_data); } i setup_psp Директива включения: Заголовок: Описание: J void setup_psp(mode) Инициализирует (mode = PSP_ENABLED) или отключа- ет (mode = PSP_DISABLED) порт PSP. Пример использования: #include <16F877.h> tffuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,parity=N,xmit=PIN Об, rcv=PIN_C7,bits=8) void main(void) { unsigned char c; setup_psp(PSP_ENABLED); while (1) { c = getc (); //Считываем символ while (!psp_output_full ()) psp_data = с; //Выводим с через PSP } } 25 — 6-767
386 Приложение Д. Библиотечные функции и макроопределения Функции для работы с интерфейсом 12С i2c_poll Директива включения: Заголовок: Описание: #use i2c() inti i2c_poll() Используется только в случае присутствия аппаратного порта SSP (интерфейс 12С в режиме Slave). Функция воз- вращает TRUE, если принят байт в буфер приема, и FALSE, если буфер приема пуст. Пример использования: #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600,parity=N,xmit=PIN_C6, rcv=PIN_C7,bits=8) fuse standard_IO(D) fruses i2c(slave,sda=PIN_C4,scl=PIN_C3, address=OxaO,FORCE_HW) void main(void) { int8 c; while (1) { //Ожидаем байта if (i2c_poll()) //в буфере приема { с = i2c_read(); //Считываем байт output_D(OxFF Л с); //Индикация } } i2c_read Директива включения: Заголовок: Описание: #use i2c() int8 i2c_read([inti ack]) Считывает байт по интерфейсу 12С. В режиме Master эта функция генерирует такт синхронизации, а в режиме Slave — ожидает этого такта (для стробирования сторо- жевого таймера во время ожидания в режиме Slave сле- дует добавить в директиву #use i2c флаг restart_wdt). Необязательный параметр ack (значе- ние 1 или 0) определяет, должно ли быть подтверждение байта при чтении. Пример использования: #include <16F877.h> #fuses HS, NOWDT fuse delay(clock=10000000) #use rs232(baud=9600, parity=N, xmit=PIN_C6, rcv=PIN_C7,bits=8) #use standard_IO(D)
Функции и макроопределения компилятора CCS-PICC 387 #uses i2c(slave,sda=PIN_C4, scl=PIN_C3, address=OxaO,FORCE_HW) void main(void) { int8 c; while(1) { //Ожидаем байта if (i2c_poll()) //в буфере приема { с = i2c_read(); //Считываем байт output_D(OxFF Л с); //Индикация } } i2c_start Директива включения: Заголовок: Описание: Пример использования: #use i2c() void i2c_start() Создает условие начала передачи, когда микроконтрол- лер находится в режиме I2C Master. После создания та- кого условия уровень сигнала в тактовой линии будет низким до тех пор, пока не будет вызвана функция i2c_write. Если до вызова функции i2c_stop функ- ция i2c_start будет вызвана еще раз, это создаст осо- бое условие рестарта. #include <16F877.h> #fuses HS, NOWDT #use delay(clock=10000000) #use rs232(baud=9600, parity=N, xmit=PIN_C6, rcv=PIN_C7,bits=8) (fuse standard_IO(D) fuses i2c(master,sda=PIN_Dl, scl=PIN_D0) void main(void) { int8 c; output_float(PIN_D0); output_float(PIN_D1); while(1) { c = getc(); //Ввод символа i2c_start(); //Условие начала передачи i2c_write(ОхАО) //Передаем адрес Slave i2c_write(с); //Передаем символ i2c_stop(); //Условие конца передачи } 25*
388 Приложение Д. Библиотечные функции и макроопределения i2c_stop Директива включения: Заголовок: Описание: #use i2c() void i2c_stop() Создает условие завершения передачи, когда микрокон- троллер находится в режиме I2C Master. Пример использования: #include <16F877.h> #fuses HS, NOWDT fuse delay(clock=10000000) #use rs232(baud=9600,parity=N,xmit=PIN_C6, rcv=PIN_C7,bits=8) #use standard_IO(D) (fuses i2c (master, sda=PIN_Dl, scl=PIN_D0) void main(void) { int8 c; output_float(PIN_D0); output_float(PIN_D1); while(1) { c = getc(); //Ввод символа i2c_start(); //Условие начала передачи i2c_write(ОхАО) //Передаем адрес Slave i2c_write(с); //Передаем символ i2c_stop(); //Условие конца передачи } i2c_write } Директива включения: Заголовок: Описание: #use i2c() inti i2c_write(int8 data) Передает байт по интерфейсу 12С. В режиме Master эта функция также генерирует тактовый сигнал, а в режиме Slave ожидает такт от ведущего устройства. Возвращает бит подтверждения (квитирования). Младший разряд первой записи после создания условия начала передачи определяет направление передачи данных: 0 — от веду- щего ведомому, 1 — от ведомого ведущему. Пример использования: #include <16F877.h>r #fuses HS, NOWDT fuse delay(clock=10000000) (fuse rs232 (baud=9600, parity=N, xmit=PIN_C6, rcv=PIN_C7,bits=8) #use standard_IO(D) (fuses i2c (master, sda=PIN_Dl, scl=PIN_D0) void main(void) { int8 c; output_float(PIN_D0); output_float(PIN DI);
Функции и макроопределения компилятора CCS-PICC 389 while(1) { с = getc(); //Ввод символа i2c_start(); //Условие начала передачи i2c__write (ОхАО) //Передаем адрес Slave i2c_write(с); //Передаем символ i2c_stop(); //Условие конца передачи } } Функции для работы с аналоговыми сигналами read adc Директива включения: Заголовок: Описание: int8 read_adc() inti6 read_adc() Считывает результат аналого-цифрового преобразова- ния, разрядность которого зависит от разрядности АЦП и параметров директивы #device adc: #device Разрядность АЦП 8 10 11 16 adc=8 O..OxFF 0..0xFF 0..0xFF 0..0xFF adc=10 — 0..0x3FF — — adc=ll — — 0..0x7FF adc=l6 0..0xFF00 0..0xFFC0 0..0xFFE0 0..0xFFF0 Пример использования: #include <18F458.h> #device ADC=10 #use delay(clock=10000000) tffuses HS, NOWDT #byte ADCONO = OxlF #int_AD //Обработка прерывания от АЦП AD_isr() { output_D(~(read_adc())); ADCONO |= 4; //Новое преобразование } void main() { setup_adc_ports(ALL_ANALOG); setup_adc(ADC_CLOCK_DIV_32); enable_interrupts(INT_AD); enable_interrupts(global); set_adc_channel(0) ; ADCONO |=4; //Начинаем преобразование while(1);
390 Приложение Д. Библиотечные функции и макроопределения set adc channel Директива включения: Заголовок: Описание: void set_adc_channel(int8 channel) Выбирает аналоговый канал channel для использова- ния при следующем вызове функции read_adc. Пример использования: #include <18F458.h> ^device ADC=10 fuse delay(clock=10000000) #fuses HS, NOWDT #byte ADCONO = OxlF #int_AD //Обработка прерывания от АЦП AD isr() { output_D(~(read_adc())); ADCONO |= 4; //Новое преобразование } void main() { setup_adc_ports(ALL_ANALOG); setup_adc(ADC_CLOCK_DIV_32); enable_interrupts(INT_AD); enable_interrupts(global); set_adc_channel(0); //Выбираем канал 0 ADCONO |= 4; //Начинаем преобразование while(1) ; } setup_adc Директива включения: Заголовок: Описание: void setup_adc(mode) Конфигурирует АЦП в соответствии со значением кон- станты mode: ADC_CLOCK_DIV_2, ADC_CLOCK_DIV_8, ADC_CLOCK_DIV_32 — деление частоты; ADC_OFF — АЦП отключен; ADC_CLOCK_INTERNAL — частота без делителя. Пример использования: #include <18F458.h> #device ADC=10 fuse delay(clock=10000000) tffuses HS, NOWDT #byte ADCONO = OxlF #int_AD //Обработка прерывания от АЦП AD isr() { output_D(~(read_adc())); ADCONO |= 4; //Новое преобразование
Функции и макроопределения компилятора CCS-PICC 391 void main() { setup__adc_ports (ALL_ANALOG) ; setup_adc(ADC_CLOCK_DIV_32); enable_interrupts(INT_AD); enable_interrupts(global); set__adc_channel ( 0) ; //Выбираем канал 0 ADCONO |= 4; //Начинаем преобразование wh i1e (1) ; } setup_adc_ports Директива включения: Заголовок: Описание: void setup_adc_ports(mode) Конфигурирует выводы АЦП в соответствии со значе- нием константы mode: - NO_ANALOGS — нет аналоговых входов; - ALL_ANALOG — АО, Al, А2, АЗ, А5, ЕО, El, Е2; опор- ное напряжение — на выводе Vdd; - ANALOG_RA3_REF — АО, Al, А2, А5, ЕО, El, Е2; опор- ное напряжение — на выводе АЗ; - А_ANALOG — АО, Al, А2, АЗ, А5; опорное напряже- ние — на выводе Vdd; - A_ANALOG_RA3_REF — АО, Al, А2, А5; опорное на- пряжение — на выводе АЗ; - RAO_RA1_RA3_ANALOG — АО, А1, АЗ; опорное на- пряжение — на выводе Vdd; - RAO_RA1_ANALOG_RA3_REF — АО, Al; опорное на- пряжение — на выводе АЗ; - ANALOG__RA3_RA2_REF — АО, Al, А5, ЕО, El, Е2; опорное напряжение — на выводах А2, АЗ; - ANALOG_NOT_RE1_RE2 — АО, А1, А2, АЗ, А5, ЕО; опорное напряжение — на выводе Vdd; - ANALOG_NOT_RE1_RE2_REF_RA3 — АО, Al, А2, А5, ЕО; опорное напряжение — на выводе АЗ; - ANALOG_NOT_RE1_RE2_REFJRA3_RA2 — АО, Al, А5; опорное напряжение — на выводах А2, АЗ; - A_ANALOG_RA3_RA2_REF — АО, Al, А5; опорное на- пряжение — на выводах А2, АЗ; - RAO_RA1_ANALOG_RA3_RA2_REF — АО, А1; опорное напряжение — на выводах А2, АЗ; - RAO_ANALOG — АО; - RAO_ANALOG_RA3_RA2_REF — АО; опорное напряже- ние — на выводах А2, АЗ. Пример использования: #include <18F458.h> ttdevice ADC=10 (fuse delay(clock=10000000) tffuses HS, NOWDT #byte ADCONO = OxlF
392 Приложение Д. Библиотечные функции и макроопределения #int_AD //Обработка прерывания от АЦП AD isr() { output_D(~(read_adc() ) ) ; ADCONO |= 4; //Новое преобразование } void main() { setup_adc_ports(ALL_ANALOG); setup_adc(ADC_CLOCK_DIV_32); enable_interrupts(INT_AD); enable_interrupts(global); set_adc_channel(0); //Выбираем канал 0 ADCONO |= 4; //Начинаем преобразование while(1); } setup__comparator Директива включения: Заголовок: Описание: void setup_comparator(mode) Конфигурирует аналоговый компаратор в соответствии со значением константы mode (части представленных ниже констант соответствуют входам С1-, С1+, С2- и С2+): - А0_АЗ_А1_А2; - А0_А2_А1_А2; - NC_NC_A1_A2; - NC_NC_NC_NC; - A0_VR_Al_VR; - A3_VR_A2_VR; - AO_A2_A1_A2_OUT_ON__A3_A4; - A3 A2 Al A2. Пример использования: #include <18F458.h> #use delay(clock=10000000) #fuses HS, NOWDT, NOPROTECT #use rs232(baud=9600,xmit=PIN_C6,rcv=P!N_C7) #byte CMCON = 0xFB4 void main() { setup_comparator(AO_VR_A1_VR); while(1) { if(!CMCON.6) puts("Voltage < 3.6 V"); else puts("Voltage > 3.6 V"); delay_ms(500) ; } }
Издательство “МК-Пресс” представляет Вольфганг Трамперт AVR-RISC микроконтроллеры (+CD) ISBN 966-8806-07-7 464 стр., твердая обложка Эта книга очень наглядно и подробно описывает архитектуру, внутреннее устройство, аппаратные ресурсы и систему команд микроконтроллеров семейства AVR. Цель книги — нау- чить читателя проектировать собственные схемы с применением микроконтроллеров прогрес- сивной RISC-архитектуры, а также разрабатывать и тестировать программы для этих проектов. Кроме всего прочего, подробно рассматривается использование программатора для за- грузки разработанных программ в память Flash-EPROM нового поколения микроконтроллеров. На прилагаемом к книге компакт-диске, кроме полнофункционального Ассемблера и эмулятора для отладки программ, находятся все исходные коды рассмотренных примеров, которые могут использоваться читателем в собственных проектах. г ЯЗ Иэжреяме» ysqwwMw* » Он Вольфганг Трамперт Измерение, управление и регулирование с помощью AVR микроконтроллеров ISBN 966-8806-14-Х 240 стр., твердая обложка Книга описывает особенности применения AVR-микроконтроллеров в технике измере- ния, управления и регулирования. При этом основной акцент поставлен на измерении напряже- ния, выводе и отображении результатов измерений, а также на регулировании аналоговых на- пряжений. Изложенный материал дает возможность поэтапно проследить весь процесс разра- ботки устройства, понять, почему программное и аппаратное обеспечение скомпоновано именно таким, а не каким-либо другим образом, и суметь в случае необходимости выполнить самостоя- тельную разработку. Книги издательства “МК-Пресс” можно заказать: по адресу: 02002, г.Киев, а/я 294, по телефону/факсу: (044) 517-73-77, по e-mail: info@mk-press.com или приобрести в магазине “Микроника” по адресу: г.Киев, ул. М.Расковой, 13 Посетите наш Internet-магазин: http://www.mk-press.com
Издательство “МК-Пресс” представляет КОМП ШТЕРНА CXEMOTEXHIKA Бабич М.П., Жуков I.A. Комп'ютерна схемотехшка ISBN 966-96415-1-9 412 стор., тверда обкладинка В цьому навчальному поабнику систематизовано викладен! 1нформац1йн1, арифметичн! i лопчж основи мжроелектронних схем сучасних комп'ютер!в. Розглянуто принципи побудови i функцюнування лопчних та запам'ятовуючих елемент)в, типових функцюнальних вузл’1в, аналого-цифрових i цифро-аналогових перетворювач!в, електронноТ пам'ял, арифметико- лопчних та керуючих пристроТв, мжропроцесор!в, 1нтерфейсних контролер!в. Розраховано на студенлв 1нженерно-техн!чних спефальностей вищих навчальних заклад!в. Бабич Н.П., Жуков И.А. Компьютерная схемотехника. Методы построения и проектирования ISBN 966-96415-2-7 576 стр., мягкая обложка В книге систематизированы и изложены основы теории и практической реализации циф- ровой схемотехники. В ней рассмотрены теоретические основы компьютерной схемотехники (информатика, арифметика, логика), методы анализа и синтеза логических и запоминающих элементов, комбинаторных и последовательностных функциональных узлов. Детально описаны основные структурные устройства современных компьютеров: процессоры, микропроцессоры, память, арифметико-логические устройства, интерфейсы и программируемые контроллеры. Отдельный раздел книги посвящен истории развития компьютерной техники, архитектуре и структуре компьютеров, принципам построения современных компьютерных систем. Книга написана доступным языком, содержит большое количество примеров и будет полезна студен- там инженерно-технических специальностей высших учебных заведений. Книги издательства “МК-Пресс” можно заказать: по адресу: 02002, г.Киев, а/я 294, по телефону/факсу: (044) 517-73-77, по e-mail: info@mk-press.com или приобрести в магазине “Микроника” по адресу: г.Киев, ул. М.Расковой, 13 Посетите наш Internet-магазин: http://www.mk-press.com
Издательство “МК-Пресс” представляет Проектнронанне и приме*енив Mk-CIpwc Швец В.А. Одноплатные микроконтроллеры. Проектирование и применение ISBN 966-96415-4-3 304 стр., мягкая обложка На базе микропроцессоров КР1810 и К1816 представлены основные методы проектирова- ния одноплатных микроконтроллеров для информационных и управляющих систем. Подробно рассматриваются вопросы проектирования узлов одноплатных микроконтроллеров, программ- ного обеспечения, применение микропроцессоров в цифровой обработке информации и изме- рительных устройствах: частотомерах, фазометрах, вольтметрах, фильтрах. В примерах пред- ставлена реализация вычислительных алгоритмов на Ассемблере. Книга будет полезна для студентов ВУЗов, изучающих дисциплины "Микро-процессоры в системах и устройствах" специальности "Радиоэлектронные устройства, системы и комплексы", "Аппаратура радиосвязи, радиовещания и телевидения”, а также для разработчиков цифровых устройств на базе микропроцессоров. Вовк П.Ю. Зарубежные электромагнитные реле ISBN 966-96415-0-0 400 стр., мягкая обложка Книга представляет собой справочное пособие по зарубежным электромагнитным реле. За основу взят материал о сигнальных, промышленных и высокочастотных реле Fujitsu, на основа- нии которых рассматривается общая теория и практика применения электромагнитных реле. В книге представлены таблицы перекрестных ссылок для аналогов всех основных зару- бежных производителей реле рассматриваемых типов. Книги издательства “МК-Пресс” можно заказать: по адресу: 02002, г.Киев, а/я 294, по телефону/факсу: (044) 517-73-77, по e-mail: info@mk-press.com или приобрести в магазине “Микроника” по адресу: г.Киев, ул. М.Расковой, 13 Посетите наш Internet-магазин: http://www.mk-press.com
Издательство “МК-Пресс” представляет Авраменко Ю.Ф. CD-проигрыватели. Схемотехника (+CD) ISBN 966-8806-13-1 352 стр., мягкая обложка В книге систематизировано изложены основные принципы и базирующиеся на них схе- мотехнические решения, используемые в формате Compact Disc Digital Audio System. В ней кратко рассмотрены методы преобразования аналогового сигнала в процессе его подготовки к записи на оптический носитель, а также представлены сведения обо всех функциональных уст- ройствах проигрывателя. На примерах конкретных микросхем даны схемотехнические решения всех систем CD-проигрывателя. На основании технической документации компаний SONY и PHILIPS представлены описания большинства микросхем, которые выпускались этим произво- дителями за последние 10 лет. Марти Браун Источники питания. Расчет и конструирование ISBN 966-8806-01-8 288 стр., мягкая обложка В книге поэтапно показано проектирование широкого набора источников питания. С ее помощью каждый, кто обладает базовыми познаниями в области электроники, сможет проекти- ровать и создавать сложные источники питания. Кроме общих промышленных подходов к конст- руированию, системно представлено проектирование линейных, импульсных и квазирезонанс- ных источников питания. Сложные вопросы, наподобие магнетизма и контроля электромагнит- ных помех, разъясняются простым и доступным языком. Включена информация о проектирова- нии выходных каскадов, о выборе ИС контроллера и других функциях, наподобие управления импульсными источниками питания, потери электрического сигнала, отключения внешнего на- пряжения и др. Рассмотрены методики формирования сигналов, уменьшения основной потери, демпферы и квазирезонансные преобразователи. Книги издательства “МК-Пресс” можно заказать: по адресу: 02002, г.Киев, а/я 294, по телефону/факсу: (044) 517-73-77, по e-mail: info@mk-press.com или приобрести в магазине “Микроника” по адресу: г.Киев, ул. М.Расковой, 13 Посетите наш Internet-магазин: http://www.mk-press.com
Издательство “МК-Пресс” представляет Авраменко Ю.Ф. Мощные биполярные транзисторы для импульсных источников питания, TV-приемников и мониторов ISBN 966-8806-17-4 544 стр., мягкая обложка В справочнике представлены электрические параметры на мощные биполярные транзи- сторы, имеющие высокую скорость переключения. Данные приборы применяются в импульсных источниках питания различного назначения, в промышленном оборудовании, в бытовой и про- фессиональной видео- и аудиотехнике. Указаны технические данные на изделия следующих ведущих производителей полупроводниковых приборов: FAIRCHILD, HITACHI, MOTOROLA (ON SEMICONDUCTOR), PANASONIC, PHILIPS, SANKEN, SAMSUNG, SANYO, SHINDENGEN, ST- MICROELECTRONICS, TOSHIBA и ZETEX. Таблица аналогов полупроводниковых приборов со- ставлена на основании руководства Master Replacement Guide. Справочник рассчитан на спе- циалистов, занимающихся сервисным обслуживанием, а также на радиолюбителей. Тяпичев А.Г. Персональный компьютер в радиолюбительской практике ISBN 966-8806-08-2 400 стр., мягкая обложка Книга предназначена для любознательного читателя и любителей мастерить своими ру- ками. В ней описаны различные варианты специального использования персонального компью- тера для выполнения «нетрадиционных» для него работ, таких как управление различными удаленными аппаратами, кодирование и декодирование различных сигналов, создание принци- пиальных электрических схем и проверка работоспособности этих схем, создание звуковых эф- фектов и многое другое. Большое внимание уделено процессу программирования микрокон- троллеров. Даны описания специальных компьютерных программ, и подробное описание аппа- ратов, которые могут подключаться к компьютеру и применяться в совместной с ним работе, приведены исходные коды простых компьютерных программ, которые можно самостоятельно дополнять и приспосабливать к своим индивидуальным нуждам. Книги издательства “МК-Пресс” можно заказать: по адресу: 02002, г.Киев, а/я 294, по телефону/факсу: (044) 517-73-77, по e-mail: info@mk-press.com или приобрести в магазине “Микроника” по адресу: г.Киев, ул. М.Расковой, 13 Посетите наш Internet-магазин: http://www.mk-press.com
Издательство “МК-Пресс” представляет Попов С.Л. BIOS и оптимизация работы ПК ISBN 966-96415-8-6 352 стр., мягкая обложка Книга посвящена описанию функционирования и настройки (BIOS) фирм Award, AMI, Phoenix и др. Рассматриваются темы диагностики, конфигурирования, настройки и оптимизации работы компьютерной системы. Вопросы повышения производительности компьютера путем разгона его отдельных компонентов изучаются сточки зрения настроек системной BIOS. Книга предназначена для пользователей среднего уровня, которые знакомы с основами устройства и принципами работы ПК и хотят повысить производительность своего компьютера путем настройки параметров работы чипсета материнской платы, шин и т. п. Книга является ру- ководством по настройкам BIOS различных версий; опытные пользователи могут использовать ее в качестве справочного пособия, в т. ч. по разгону компьютера средствами BIOS. FreeBSD Дидок А.А. Один на один с FreeBSD (+2CD) ISBN 966-8806-00-Х 704 стр., твердая обложка Эта книга является исчерпывающим руководством системного администратора. Она ос- вещает вопросы установки, настройки и практического использования в сети операционной сис- темы FreeBSD. Автор на основании своего практического опыта работы сетевым администрато- ром в различных крупных компаниях показывает множество аспектов администрирования FreeBSD, включая работу с командными интерпретаторами, управление пользователями и учетными записями, управление файловой системой, использование пакетов приложений и де- рева портов, настройка ядра системы, работа в сети и безопасность системы, настройка таких служб и средств как DNS, FTP, NIS, NFS, SAMBA, DHCP, Web-сервер Apache и многое другое. Книги издательства “МК-Пресс” можно заказать: по адресу: 02002, г.Киев, а/я 294, по телефону/факсу: (044) 517-73-77, по e-mail: info@mk-press.com или приобрести в магазине “Микроника” по адресу: г.Киев, ул. М.Расковой, 13 Посетите наш Internet-магазин: http://www.mk-press.com
Издательство “МК-Пресс” представляет Конахович Г.Ф. Защита информации в телекоммуникационных системах "’ЗАЩИТА ИНФОРМАЦИИ , .жтвтжоииуиикяцжжиых »• ; "ейстемах. •. ISBN 966-8806-03-4 288 стр., мягкая обложка В этой книге рассмотрены основные проблемы защиты информации, возникающие в ве- домственных системах связи и передачи данных, радиотехнических системах и системах связи общего пользования. Проведен достаточно полный анализ каналов утечки информации, мето- дов и способов несанкционированного получения информации, средств защиты информации. Проанализированы особенности функционирования ведомственных телекоммуникационных систем. Издание будет полезно специалистам в области телекоммуникаций и защиты информа- ции, а также студентам соответствующих специальностей. Конахович Г.Ф., Пузыренко А.Ю. Компьютерная стеганография. Теория и практика ISBN 966-8806-06-9 288 стр., мягкая обложка Эта книга — одно из первых изданий в области стеганографии. В ней изложены теорети- ческие и практические основы компьютерной стеганографии. Рассмотрены особенности и пер- спективы использования современной системы символьной математики MathCAD v.12 в целях стеганографической защиты информации. Системно изложены проблемы надежности и стойко- сти произвольной стегано-графической системы по отношению к различным видам атак, а также оценки пропускной способности канала скрытого обмена данными. Представлены результаты существующих информационно-теоретических исследований проблемы информационного со- крытия в случае активного противодействия нарушителя. В книге изложены известные стегано- графические методы, направленные на скрытие конфиденциальных данных в компьютерных файлах графического, звукового и текстового форматов. Книги издательства “МК-Пресс” можно заказать: по адресу: 02002, г.Киев, а/я 294, по телефону/факсу: (044) 517-73-77, по e-mail: info@mk-press.com или приобрести в магазине “Микроника” по адресу: г.Киев, ул. М.Расковой, 13 Посетите наш Internet-магазин: http://www.mk-press.com
ББК 32.973-04 LU83 УДК 004.312 Ш83 Програмування на мов! С для AVR та PIC м!кроконтролер1в. / Уклад. Ю.О. Шпак — К.: “МК-Пресс”, 2006. — 400 с., in. ISBN 966-8806-16-6 В книз1 розглянуто програмування на mobi С мжроконтролер!в AVR з використанням компшятора WinAVR, а також мжроконтролер!в PIC з використанням компшятора CCS-PICC. Коротко розглянуто архитектуру та апаратне забезпечення мжроконтролер!в AVR та PIC. Описано засоби програмноТ розробки в середовищ! WinAVR та CCS-PICC, включаючи емуляц!ю програм за допомогою AVR Studio та MPLAB. Коротко розглянуто стандартний синтаксис мови С та директиви препроцесора, а також особливосл програмування на Ц|й мов! для мжроконтролер!в. Книга мютить багато програмних приклад!в на С, а також довщник з описом системи асемблерних команд м1кроконтролер1в AVR та PIC. ББК 32.973-04 ISBN 966-8806-16-6 © "МК-Пресс", 2006
Книга посвящена программированию на языке С микроконтроллеров AVR с использованием компилятора WinAVR, а также микроконтроллеров PIC с использованием компилятора CCS-PICC. Рассмотрены следующие вопросы: архитектура и аппаратное обеспечение микроконтроллеров AVR и PIC; - средства программной разработки в среде WinAVR и CCS-PICC, включая эмуляцию программ с помощью AVR Studio и MPLAB; - синтаксис языка С и директивы препроцессора; - особенности программирования микроконтроллеров; - программные примеры для микроконтроллеров AVR и PIC. Кроме того, книга содержит описание системы команд микроконтроллеров AVR и PIC, а также стандартных библиотечных функций языка С. ги-еэарор'ммлл На компакт-диске: - WinAVR, AVR Studio; - CCS-PICC, MPLAB; - исходные коды программ; - спецификации микросхем.
на языке ля AVR и PIC