Справочник по программированию «Bascom-AVR» (М.Л. Кулиш). Справочник по программированию Bascomavr Создание микропроцессорной системы Теория
Скачать 1.18 Mb.
|
'--------------------- ' ...... 'установка индексного регистра Z для считывания байтовых данных Ldi R30 , Low(Tab_phs * 2) 'записать адрес метки Tab_phs в индексный регистр Z Ldi R31 , High(Tab_phs * 2) 'младший и старший байты ' ...... 'установка специального индексного регистра компилятора Restore Tab_phs 'запись указателя на метку в регистры R8 и R9 ' ...... Tab_phs: Data 55 , 55 , 55 , 56 'поля данных ' ...... M12: Read Tmpb 'считывание данных ============================================================================= 24 ===================================== Справочник по программированию «Bascom-AVR» == Индексы, получаемые в приведенном выше примере, предполагают побайтную адресацию (соответствуют нумерации байт). Имеется еще одна форма загрузки адреса метки, предполагающая получение адреса перехода для его загрузки в программный счетчик. ' ...... 'установка индексного регистра Z для операции перехода Ldi R30 , Low(M12 * 1) 'записать адрес метки M12 в индексный регистр Z Ldi R31 , High(M12 * 1) 'младший и старший байты как адрес перехода Ijmp Z 'переход по считанному адресу ' ...... - следует упомянуть об аналогичных операторах, недопустимых в программах прерывания. Вот они '--------------------- Dim W As Word '--------------------- ' ...... W = Varptr(fldb) 'запись в переменную W указателя на переменную fldb W = Loadlabel(Tab_phs) 'запись в переменную W указателя на метку ' ...... Типовые программы прерывания Далее приведены примеры программ обработки прерываний различного типа. Пример 1 - Прерывание таймера '---------------------------------------------------------------------------------- Dim R_tim As Byte 'счетчик временных интервалов, задаваемых в главной программе ' ...... On Timer0 Timer0_int Nosave 'вектор прерывания от таймера 0 ' ...... Tccr0 = &B00000101 'режим таймера 0: Fкв/1024 - часы реального времени Enable Timer0 'разрешить прерывание таймера Enable Interrupts 'разрешить прерывания ' ...... '---------------------------------------------------------------------------------- 'обработка прерывания таймера 0 (8-битный таймер) 'используется как часы реального времени с частотой 10 мс 'для этого организована схема деления 8 МГц/1024/78 Timer0_int: $asm Push R31 'сохраним регистры In R31 , Sreg Push R31 '----- Ldi R31 , 178 'переустановим счетчик Out Tcnt0 , R31 '----- Lds R31 , {r_tim} 'обработка счетчика переменных временных интервалов And R31 , R31 Breq Intt0_e Dec R31 Sts {r_tim} , R31 '----- Intt0_e: Pop R31 Out Sreg , R31 'восстановим регистры Pop R31 $end Asm Return '---------------------------------------------------------------------------------- Пример 2 - Прерывание от изменения уровня на линии порта '---------------------------------------------------------------------------------- Dim Edat As Byte 'признак "Есть новые данные энкодера" Dim R_cdec As Byte 'СЧЕТЧИК ЩЕЛЧКОВ ВЛЕВО Dim R_cinc As Byte 'СЧЕТЧИК ЩЕЛЧКОВ ВПРАВО ' ...... On Int0 Int0_int Nosave 'вектор внешнего прерывания 0 ' ...... '--------------------- Mcucr = &B10000010 'IDLE - разрешить, INT0 - по спаду '--------------------- ' ...... Enable Int0 'разрешить внешнее прерывание 0 ============================================================================= 25 ===================================== Справочник по программированию «Bascom-AVR» == Enable Interrupts 'разрешить прерывания ' ...... '---------------------------------------------------------------------------------- 'ОБРАБОТКА ВНЕШНЕГО ПРЕРЫВАНИЯ 0, ОБСЛУЖИВАЮЩЕГО ДВУХФАЗНЫЙ ЭНКОДЕР 'ПРЕРЫВАНИЕ ПРОИСХОДИТ ПО СПАДУ НА ВХОДЕ P_CHA. ЗАТЕМ ПРОГРАММА ИНТЕГРИРУЕТ '(ФИЛЬТРУЕТ) ЭТОТ УРОВЕНЬ, ДОЖИДАЯСЬ НАДЕЖНОГО УСТАНОВЛЕНИЯ "0". В МОМЕНТ 'ФИКСАЦИИ "0" НА ЛИНИИ P_cha РЕГИСТРИРУЕТСЯ УРОВЕНЬ P_chb , ПО КОТОРОМУ И 'ОПРЕДЕЛЯЕТСЯ НАПРАВЛЕНИЕ ВРАЩЕНИЯ '------------------------------------------- ' P_cha Alias PIND.2 'ОПРЕДЕЛЕНИЕ ПОРТОВ ВВОДА КОДА ЦИФРОВОЙ РУЧКИ ' P_chb Alias PIND.3 '------------------------------------------- Const Con_irt = 30 'КОНСТАНТА ИНТЕГРИРОВАНИЯ УРОВНЯ НА ВХОДЕ '--------- Int0_0: Inc R31 'УРОВЕНЬ ВЫСОКИЙ - ОТКАТ СЧЕТЧИКА Cpi R31 , 45 'это Con_irt*1.5 Brne Int0_1 'ЕСЛИ ЖДЕМ СЛИШКОМ ДОЛГО - ВЫХОД Rjmp Int0_e '--------- 'ВНЕШНЕЕ ПРЕРЫВАНИЕ ОТ ЦИФРОВОЙ РУЧКИ Int0_int: $asm Push R31 'сохраним регистры In R31 , Sreg Push R31 '----- Ldi R31 , 30 'ПОСТОЯНАЯ ИНТЕГРИРОВАНИЯ Int0_1: Sbic Pind , 2 Rjmp Int0_15 'ЕСЛИ НА НОЖКЕ "1" Dec R31 'ПОКА НЕ ОБНУЛИЛСЯ СЧЕТЧИК ЖДЕМ Brne Int0_1 'УСТАНОВЛЕНИЯ НИЗКОГО УРОВНЯ Rjmp Int0_2 '----- Int0_15: Inc R31 'УВЕЛИЧИВАЕМ СЧЕТЧИК Cpi R31 , 60 'ЕСЛИ НА "1" УСТАНОВЛЕНА СЛИШКОМ ДОЛГО Brne Int0_e 'НА ВЫХОД Rjmp Int0_1 '--------------------------------------------- 'ПРОГРАММА РАСЧИТАНА НА КОДОВЫЙ ПЕРЕКЛЮЧАТЕЛЬ 'С НОРМАЛЬНО РАЗОМКНУТЫМИ КОНТАКТАМИ, ЗАМЫКАЮЩИМИСЯ 'ТОЛЬКО В МОМЕНТ ЩЕЛЧКА. ЭТО ОЧЕНЬ УПРОЩАЕТ ЗАДАЧУ '--------------------------------------------- ' |<----- ВРАЩЕНИЕ ВЛЕВО (ИЛИ НАОБОРОТ, В ЗАВИСИМОСТИ ОТ МОНТАЖА) 'chA--- --------- ------ ОПРЕДЕЛЕНИЕ НАПРАВЛЕНИЯ ' ¦ ¦ chA ¦ ¦ ВРАЩЕНИЯ ЦИФРОВОГО КОДЕРА ' --- --- ОСНОВАНО НА АНАЛИЗЕ СОСТОЯНИЯ 'chB----- | --------- ---- КАНАЛА "B" В МОМЕНТ ЗАМЫКАНИЯ ' | ¦ | ¦ chB ¦ ¦ (ЩЕЛЧКА) В КАНАЛЕ "A" ' | --- --- ' | |<------ ВРАЩЕНИЕ ВПРАВО '--------------------------------------------- Int0_2: 'ОБРАБОТКА ИЗМЕНЕНИЯ УРОВНЯ '--------- Sbis Pind , 3 'ОПРЕДЕЛИМ НАПРАВЛЕНИЕ ВРАЩЕНИЯ Rjmp Int0_3 '--------- 'ВРАЩЕНИЯ ВЛЕВО (УМЕНЬШЕНИЕ) Lds R31 , {r_cdec} 'УВЕЛИЧИМ СЧЕТЧИК ЩЕЛЧКОВ ВЛЕВО Inc R31 Sts {r_cdec} , R31 Rjmp Int0_4 '--------- Int0_3: 'ВРАЩЕНИЯ ВПРАВО (УВЕЛИЧЕНИЕ) Lds R31 , {r_cinc} 'УВЕЛИЧИМ СЧЕТЧИК ЩЕЛЧКОВ ВПРАВО Inc R31 Sts {r_cinc} , R31 Int0_4: Ldi R31 , &HFF Sts {edat} , R31 'поставить признак "НОВЫЕ ДАННЫЕ ЕНКОДЕРА" '--------- Int0_e: Pop R31 Out Sreg , R31 Pop R31 ============================================================================= 26 ===================================== Справочник по программированию «Bascom-AVR» == $end Asm Return '---------------------------------------------------------------------------------- Пример 3 - Прерывание АЦП при завершении преобразования '---------------------------------------------------------------------------------- Dim Tmpb As Byte 'временные байтовые данные Dim B_adc As Byte 'указатель "есть новые данные внутреннего АЦП" Dim Dadc As Word 'данные АЦП (внутреннего) Dim Ua As Single 'временное значение ' ...... Mcucr = &B10000000 'IDLE – разрешить ' ...... '------------------------------------------ ' измерение с помощью внутреннего АЦП результат Ua, выражен в Вольтах шкалы Rd_iadc: Admux = &B00000001 'внешняя опора АЦП со входа AREF, внутренняя выключена 'измерение по входу PA1 (напряжение) Adcsr = &B10001110 'разрешить АЦП с частотой тактирования F / 64 в режиме 'с естественным положением битов, прерывание разрешено '------- For Tmpb = 1 To 16 'произвести 16 измерений Set Adcsr.6 'запустить АЦП Rdiadc1: Idle 'останов If B_adc = 0 Then 'есть данные внутреннего АЦП? Goto Rdiadc1 'нет - повторить End If Next Tmpb Adcsr = &B00000110 'запретить АЦП Dadc = Dadc - 25 'коррекция смещения нуля Ua = Dadc : Dadc = 0 'в формат с плавающей точкой, а исходный очистить Ua = Ua * 0.0003052 : Return 'привести к шкале 0...5 В '------------------------------------------ 'обработка прерывания от внутреннего АЦП Adc_int: $asm Push R31 'сохраним регистры In R31 , Sreg Push R31 Push R30 Push R29 '----- 'считать данные внутреннего АЦП lds R29 , {Dadc} 'считать сумму Lds R30 , {Dadc + 1} In R31 , Adcl 'считать показани Add R29 , R31 'добавить к сумме показания АПЦ In R31 , Adch Adc R30 , R31 Sts {Dadc} , R29 Sts {Dadc + 1} , R30 '----- Ldi R31 , 255 'есть данные внутреннего АЦП Sts {B_adc} , R31 '----- Adcinte: Pop R29 'восстановим регистры Pop R30 Pop R31 Out Sreg , R31 Pop R31 Reti $end Asm Return '---------------------------------------------------------------------------------- Пример 4 - Прерывание приемника и передатчика UART '---------------------------------------------------------------------------------- Dim Rdat As Byte 'признак "Есть новые данные из RS" Dim Cnt_rc As Byte 'счетчик принимаемых из RS символов Dim R_cch As Word 'указатель буфера принимаемых символов Dim Bufrr As String * 20 'принятой строки из RS Dim Bufr As String * 20 'обрабатываемой строки из RS Dim T_cch As Word 'указатель буфера передаваемых символов Dim Buft As String * 20 'передаваемая строка ============================================================================= 27 ===================================== Справочник по программированию «Bascom-AVR» == ' ...... '--------------------- ' UART: $baud = 9600 'скорость 9.6 кБ $crystal = 8000000 'при кварце 8 МГц '--------------------- ' назначение векторов прерывания On Urxc Rxd_int Nosave 'вектор прерывания от приемника UART On Utxc Txd_int Nosave 'вектор прерывания от приемника UART '--------------------- Gosub Clr_bufrr 'очистить переменные приемного буфера Enable Urxc 'разрешить прерывание приемника UART Enable Utxc 'разрешить прерывание передатчика UART Enable Interrupts 'разрешить прерывания '--------------------- ' ...... Buft = "TRANSFERED DATA" 'передаваемая строка Gosub Print_tb 'передать буфер ' ...... '---------------------------------------------- ' обработка прерывания приемника UART Rxd_int: $asm Push R31 'сохраним регистры In R31 , Sreg Push R31 Push R30 Push R27 Push R26 '--------- 'принимаем данные интерфейса Lds R26, {R_cch} 'значение текущей позиции буфера Lds R27, {R_cch + 1} In R31 , Udr 'считать принятый символ 'преобразование строчного символа в прописной Andi R31 , &h7f 'ограничение диапазона символов Cpi R31 , &h61 'ниже кода символа 'a'? (нижняя граница) Brcs Rxdc_0 'да - переход Cpi R31 , &h7b 'выше кода символа 'z'? (верхняя граница) Brcc Rxdc_0 'да - переход Andi R31 , &h5f 'между ними - наложить маску '----- Rxdc_0: Cpi R31 , &H0A 'CR? Breq Rxdc_e 'игнорировать Cpi R31 , &H0D 'LF? Breq Rxdc_2 'это конец строки St X+ , R31 'остальное записывать в буфер Lds R30, {Cnt_rc} Inc R30 Cpi R30 , 20 'число 20 – длина буфера Breq Rxdc_e 'если не последняя позиция Rxdc_1: Sts {r_cch} , R26 'записать измененный указатель буфера Sts {r_cch + 1} , R27 Sts {cnt_rc} , R30 'и новое значение счетчика символов Rxdc_e: Pop R26 Pop R27 Pop R30 Pop R31 Out Sreg , R31 Pop R31 Reti '--------- 'принят конец строки – нужно переписать принятые данные в промежуточный буфер Rxdc_2: Ldi R31 , &HFF Sts {rdat} , R31 'поставить признак наличия принятой строки в буфере Bufr '--------- 'займем еще регистры (Y) для копирования из Bufrr в Bufr Push R29 'сохраним их Push R28 $end Asm Loadadr Bufr , X 'указатель буфера Bufr в Y на начало $asm Mov R28 , R26 'применим такой извратный способ, чтобы компилятор Mov R29 , R27 'не увидел явного использования программного стека $end Asm ============================================================================= 28 ===================================== Справочник по программированию «Bascom-AVR» == Loadadr Bufrr , X 'указатель буфера Bufrr в X на начало $asm Lds R30, {Cnt_rc} 'считать число принятых символов Rxdc_c: Ld R31 , X+ 'считать из Bufrr St Y+ , R31 'записать в буфер Bufr Dec R30 Brne Rxdc_c St Y , R30 'записать в буфер Bufr конец строки Pop R28 'восстановим указатель программного стека Pop R29 '--------- $end Asm Loadadr Bufrr , X 'указатель буфера Bufrr в X снова на начало $asm Rjmp Rxdc_1 'и сбросить значение счетчика символов $end Asm Return 'пустая команда, на место которой компилятор 'поставит обязательную команду RETI '---------------------------------------------- ' обработка прерывания передатчика UART Txd_int: Push R31 'сохраним регистры In R31 , Sreg Push R31 Push R27 Push R26 '----- Lds R26 , {t_cch} 'значение текущей позиции буфера Lds R27 , {t_cch + 1} Ld R31 , Y+ And R31 , R31 'проверить на нуль Breq Txdint1 'если он равен 0 - переход !Out Udr , R31 'записать передаваемый символ Sts {t_cch} , R26 'записать измененный указатель буфера Sts {t_cch + 1} , R27 '----- Txdint1: Pop R27 'восстановим регистры Pop R26 Pop R31 !Out Sreg , R31 Pop R31 Reti $end Asm Return '---------------------------------------------- ' ...... '---------------------------------------------- 'передача буфера Print_tb: Buft = Buft + Chr(13) + Chr(10) 'добавить к содержимому буфера команды конца строки T_cch = Varptr(buft) 'установить указатель на начало буфера Udr = Peek(t_cch) 'инициализировать начало передачи, загрузив 'первый символ в передатчик это можно было бы не делать, 'если бы можно установить бит TXC (Ucsra , 6), 'а использовать бит UDRE (Ucsra , 5) нельзя Incr T_cch 'установить указатель на следующий символ Return '---------------------------------------------- 'очистить переменные приемного буфера Clr_bufrr: Bufrr = "" 'сам буфер (записать нуль в начало) Rdat = 0 'признак наполнения Cnt_rc = 0 'счетчик принятых символов R_cch = Varptr(bufrr) 'указатель буфера на начало Return '---------------------------------------------------------------------------------- Разумеется, что при отсутствии необходимости экономить ресурсы микроконтроллера программа прерыва- ния может быть выполнена без ассемблерных вставок. Ниже приведен пример программы, содержащей такой об- работчик прерываний. Сохранение регистров общего назначения выполняется компилятором самостоятельно (оп- ция NOSAVE не применена) и все операции обработки данных производятся с использованием операторов Bas- com-AVR. ============================================================================= 29 ===================================== Справочник по программированию «Bascom-AVR» == Пример 5 - Прерывание от изменения уровня на линиях порта '---------------------------------------------------------------------------------- $regfile = "m644def.dat" 'для чипа ATMega644 ' ...... Dim Num_port As Byte 'номер порта с изменившимся уровнем Dim Dub_pa As Byte 'дублер порта A Dim Dub_pb As Byte 'дублер порта B Dim Dub_pc As Byte 'дублер порта C Dim Dub_pd As Byte 'дублер порта D |