Страница 1 из 1
ATmega8A учусь использовать прерывания
Добавлено: Ср янв 14, 2015 12:07:45
sashavir
Люди добрые, ткните носом, что делаю не так, только изучаю ассемблер... Цель стояла сделать так, чтобы на порту B горел светодиод 2 сразу, а по нажатию на кнопку соединяющую землю и вывод INT0 на микроконтроллере загоралать вторая лампочка. В моем нынешнем варианте, они просто моргают поочередно...
Код: Выделить всё
.def temp=r16 ;меняю имя регистра, не с глубогим смыслом, а чтобы поэксперементировать
.cseg
.org 0 ;назначаю адрес и куда загружать
rjmp reset ;перечисляю все прерывания для ATmega8a из даташита и назначаю им метки
rjmp _int0
rjmp _int1
rjmp timer2_comp
rjmp timer2_ovf
rjmp timer1_capt
rjmp timer1_compa
rjmp timer1_compb
rjmp timer1_ovf
rjmp timer0_ovf
rjmp spi_stc
rjmp usart_rxc
rjmp usart_udre
rjmp usart_txc
rjmp _adc
rjmp ee_rdy
rjmp ana_comp
rjmp twi
rjmp spm_rdy
reset: ;тут перечисляю метки, выдрано с обучалки с этого сайта, не совсем пока разабрался зачем все перечислять
;_int0: ;тут не уверен, что правильно сделал... Получается закоментил или просто убрал _int0, чтобы вынести его отдельно
_int1:
timer2_comp:
timer2_ovf:
timer1_capt:
timer1_compa:
timer1_compb:
timer1_ovf:
timer0_ovf:
spi_stc:
usart_rxc:
usart_udre:
usart_txc:
_adc:
ee_rdy:
ana_comp:
twi:
spm_rdy:
reti ;выход из обработчика прерываний
_int0: ;начало программы по внешнему воздействию на порт PD2
ldi temp,0b00000001 ;здесь меняю биты ISC00 на 1 и ISC01 на 0, т.е. любая смена логического состояния, если я правильно разобрался
out MCUCR,temp ;здесь загружаю биты в регистр
ldi r17,0b11111111
out DDRB,r17 ;настройка порта B на вывод
ldi r18,0b00000001
out portb,r18 ;зажигаю первую лампочку
sei ;разрешаю прерывания, тут совсем не уверен, что делаю правильно...
reti ;выход из обработчика прерываний
ldi r17,0b11111111 ;основная программа зажигающая второй светодиод
out DDRB,r17
ldi r18,0b00000010
out portb,r18
sleep ;воткнул просто ради эксперемента
Re: ATmega8A учусь использовать прерывания
Добавлено: Ср янв 14, 2015 12:13:32
НАПАЛМ
Это весь код? Где инициализация стека, для начала?
Re: ATmega8A учусь использовать прерывания
Добавлено: Ср янв 14, 2015 12:17:35
sashavir
Да я ж говорю, я пока полный чайник

Тут нужно инициализировать стек обязательно? Если да, то пойду курить про стек, я еще просто до разбора стека не дошел?
Re: ATmega8A учусь использовать прерывания
Добавлено: Ср янв 14, 2015 12:37:10
НАПАЛМ
Да, обязательно.
http://easyelectronics.ru/category/avr-uchebnyj-kurs
Используйте этот курс, начните с самого начала - там где работа с AVR студией. Всё очень просто и понятно объясняется.
Re: ATmega8A учусь использовать прерывания
Добавлено: Ср янв 14, 2015 14:32:48
Engineer_Keen
sashavir писал(а):Люди добрые, ткните носом, что делаю не так, только изучаю ассемблер...
Только начали изучать и сразу полезли в прерывания? Вам бы сначала просто светодиодом поморгать, осознать принцип и структуру программ на ассемблере. В общем вас правильно послали, но не совсем точно. Точнее будет наверно примерно
туда, ну +- пара постов.
По порядку.
Спойлер
Код: Выделить всё
rjmp reset ;перечисляю все прерывания для ATmega8a из даташита и назначаю им метки
rjmp _int0
rjmp _int1
rjmp timer2_comp
rjmp timer2_ovf
rjmp timer1_capt
rjmp timer1_compa
rjmp timer1_compb
rjmp timer1_ovf
rjmp timer0_ovf
rjmp spi_stc
rjmp usart_rxc
rjmp usart_udre
rjmp usart_txc
rjmp _adc
rjmp ee_rdy
rjmp ana_comp
rjmp twi
rjmp spm_rdy
Можно, но чтобы не загромождать программу лучше писать только нужные метки:
Код: Выделить всё
.org 0
RJMP RESET
.org INT0addr
RJMP INT0_A
RESET:
Кто-то скажет, что если сработает прерывание для которого нет вектора, то заглушка RETI поможет (как это почти сделано у вас). Я скажу что надо уметь пользоваться отладкой и исключать такие ошибки, а не ставить костыли.
Спойлер
Код: Выделить всё
reset: ;тут перечисляю метки, выдрано с обучалки с этого сайта, не совсем пока разабрался зачем все перечислять
;_int0: ;тут не уверен, что правильно сделал... Получается закоментил или просто убрал _int0, чтобы вынести его отдельно
_int1:
timer2_comp:
timer2_ovf:
timer1_capt:
timer1_compa:
timer1_compb:
timer1_ovf:
timer0_ovf:
spi_stc:
usart_rxc:
usart_udre:
usart_txc:
_adc:
ee_rdy:
ana_comp:
twi:
spm_rdy:
reti ;выход из обработчика прерываний
Да, вот эти самые заглушки. Но реализация кривая, т.к. первой строкой выполнится всегда rjmp reset, а после метки ресет у вас первой командой (после кучи меток-затычек) идет reti, это уже ошибка, т.к. 1)стек не инициализирован 2)даже при инициализированном стеке не было вызова RCALL или прерывания. Это может привести просто к сбросу или переходу на случайное место, смотря что будет в стеке
Спойлер
Код: Выделить всё
_int0: ;начало программы по внешнему воздействию на порт PD2
Допустим правильно...
Код: Выделить всё
ldi temp,0b00000001 ;здесь меняю биты ISC00 на 1 и ISC01 на 0, т.е. любая смена логического состояния, если я правильно разобрался
out MCUCR,temp ;здесь загружаю биты в регистр
Настраивать прерывание и условия его возникновения нужно не в самом прерывании, а в инициализации программы. Подозреваю, что вы не совсем понимаете, как это работает...
И сразу советую отучаться писать магические цифры (0b00000001) при настройке регистров, пишите так:
Код: Выделить всё
ldi temp,(1<<ISC00)|(0<<ISC01) ;так сразу понятно о каких битах речь
Код: Выделить всё
ldi r17,0b11111111
out DDRB,r17 ;настройка порта B на вывод
Не обязательно каждый раз настраивать порт на вывод, если это уже сделано при инициализации программы
Код: Выделить всё
ldi r18,0b00000001
out portb,r18 ;зажигаю первую лампочку
sei ;разрешаю прерывания, тут совсем не уверен, что делаю правильно...
reti ;выход из обработчика прерываний
Нет смысла ставить SEI перед RETI, т.к RETI=RET+SEI.
Спойлер
Код: Выделить всё
ldi r17,0b11111111 ;основная программа зажигающая второй светодиод
out DDRB,r17
ldi r18,0b00000010
out portb,r18
Вот что должно было быть после метки reset! Тут же и настройка прерываний и стека, и режима энергосбережения, раз уж:
И то не совсем верно. Даже если и заснет, что будет после возврата из прерывания? Переход на следующую команду после sleep, а там что? Ничего, пустая флеш-память (0xFF). Результат - некорректный опкод и сброс контроллера. Должен быть обязательно хотя бы пустой цикл
Sleep можно в него запихнуть, если надо...
В общем общая структура такая:
Код: Выделить всё
0) .include, если нужны кроме описания конкретного контроллера
1) RJMP RESET (JMP для больших контроллеров)
2) векторы прерываний, если нужны, т.е. таблица переходов JMP\RJMP, если обработчик помещается, можно не переходить и писать его прямо тут
3) конец векторов прерываний
---------------------------------
далее в произвольном порядке:
а) метка reset и все что касается инициализации, там же главный цикл программы (например пустой)
б) процедуры прерываний
в) остальные процедуры
г) остальные include, если исходник из нескольких частей.
Re: ATmega8A учусь использовать прерывания
Добавлено: Ср янв 14, 2015 15:45:23
sashavir
Engineer_Keen, НАПАЛМ Огромное спасибо за расшифровку и что показали, что почитать, изучаю... После Ревича (не в обиду ему

), что-то совсем каша, по Вашим ссылкам материал понятней разъяснен, да и в обучалке статейка хорошая...
Re: ATmega8A учусь использовать прерывания
Добавлено: Пт янв 16, 2015 12:04:46
sashavir
Я опять к Вам друзья, вроде простые вещи, а как застопаришься на одном и ни туда и ни сюда... Работаю в отладчике atmel studio 6.2 может просто не совсем правильно пользуюсь им, а может ошибка в программе, я уж не знаю... Но суть в следующем, не работает так как я хочу прерывание, нажимаю start debugging and break и по F11 прокручиваю всю программу до зацикливания в метке M1, потом добавляю руками в IO View бит RXC, что должно вызвать по логике прерывание и перепрыгнуть на метку USART_RXC, потом выйти и вернуться к моему циклу, но ничего не происходит, тупо бит по нажатию F11 пропадает и прыжка не происходит, т.е. просто продолжает работать цикл. Постарался подробно описать, помогите понять, что делаю не так, уже часа два сижу
Код: Выделить всё
.include "m8adef.inc" ;хотя, как я понял эта запись в 6-ой студии, как собаке пятая нога, он его похоже тянет автоматом
;= Start macro.inc ==============
; Тут будут макросы, потом.
;= End macro.inc ================
; RAM =========
.DSEG ; Сегмент ОЗУ
; FLASH =======
.CSEG
rjmp RESET ; Reset Handler
nop ;rjmp EXT_INT0 ; IRQ0 Handler
nop ;rjmp EXT_INT1 ; IRQ1 Handler
nop ;rjmp TIM2_COMP ; Timer2 Compare Handler
nop ;rjmp TIM2_OVF ; Timer2 Overflow Handler
nop ;rjmp TIM1_CAPT ; Timer1 Capture Handler
nop ;rjmp TIM1_COMPA ; Timer1 CompareA Handler
nop ;rjmp TIM1_COMPB ; Timer1 CompareB Handler
nop ;rjmp TIM1_OVF ; Timer1 Overflow Handler
nop ;rjmp TIM0_OVF ; Timer0 Overflow Handler
nop ;rjmp SPI_STC ; SPI Transfer Complete Handler
rjmp USART_RXC ; USART RX Complete Handler
nop ;rjmp USART_UDRE ; UDR Empty Handler
nop ;rjmp USART_TXC ; USART TX Complete Handler
nop ;rjmp ADC ; ADC Conversion Complete Handler
nop ;rjmp EE_RDY ; EEPROM Ready Handler
nop ;rjmp ANA_COMP ; Analog Comparator Handler
nop ;rjmp TWSI ; Two-wire Serial Interface Handler
nop ;rjmp SPM_RDY ; Store Program Memory Ready Handler
;Тут простите за такую корявую таблицу, я знаю, что запись кривая, просто выдрал из даташита и на скорую руку поправил,
;адресацию вроде сохранил, использовал разные способы представления таблицы, в том числе с абсолютной адресацией и c
;метками один черт, все равно не работает так, как я себе это представляю...
RESET: ldi r16,high(RAMEND) ;инициализация стека
out SPH,r16
ldi r16,low(RAMEND)
out SPL,r16
sei ;разрешить прерывания
LDI r17,(1<<RXCIE) ;это взято с уроков http://easyelectronics.ru/, смысл в слудующем, чтобы при ручном вводе
OUT UCSRB,r17 ;в отладчике "atmel studio 6.2" бита RXC срабатовало прерывание и осуществлялся прыжок на
;на метку USART_RXC: потом выход с прерывация и возврат к зацикленной программе м1
m1:
nop ;тут просто зацикленный кусок программы
nop
nop
nop
rjmp m1
USART_RXC: ;метка на прерывание
nop
nop
nop
reti ;выход из прерывания и возвращение к адресу прерванного прерывание, если опять же я правильно
;понял работу оператора reti
Re: ATmega8A учусь использовать прерывания
Добавлено: Пт янв 16, 2015 12:26:03
Engineer_Keen
sashavir писал(а):ничего не происходит, тупо бит по нажатию F11 пропадает и прыжка не происходит, т.е. просто продолжает работать цикл.
Студия 4.17. Все работает как надо, в прерывание заходит, даже наоборот бит всегда остается взведен (чтобы убрался, надо UDR считать в прерывании)
Или глюк 6й студии, или вы что-то не так нажимаете...
Re: ATmega8A учусь использовать прерывания
Добавлено: Пт янв 16, 2015 12:43:42
sashavir
Да, действительно в студии 4.14 все отлично работает... Что за ерунда, не понимаю... А в 6.20 нифига, просто не реагирует на этот бит отладчик...
Вот тебе и выражение "Новое, враг надежного"...
А в 4.17 уже есть ATmega8a или тоже только ATmega8, я так понимаю там отличия только в тактовой частоте и все?
Re: ATmega8A учусь использовать прерывания
Добавлено: Вт янв 20, 2015 15:47:27
sashavir
Подскажите, как вы боритесь с дребезжанием кнопки, вроде подтягивающий резистр воткнул на 10К, а все равно прыжок, то через одну лампочку, то через 3

Фильтр сделать не могу, по причине банального отсутствия конденсаторов (временно).
Может я в коде неправильно что делаю...
Вот программа
Спойлер
Код: Выделить всё
.include "c:\avr\project\m8adef.inc"
.def temp=r16
.def temp1=r17
;= Start macro.inc ==============
;= End macro.inc ================
; RAM =========
.DSEG
; FLASH =======
.CSEG
.ORG 0 ; (RESET)
RJMP Reset
.ORG INT0addr
RJMP button ; (INT0) External Interrupt Request 0
.ORG INT1addr
RETI ; (INT1) External Interrupt Request 1
.ORG OC2addr
RETI ; (TIMER2 COMP) Timer/Counter2 Compare Match
.ORG OVF2addr
RETI ; (TIMER2 OVF) Timer/Counter2 Overflow
.ORG ICP1addr
RETI ; (TIMER1 CAPT) Timer/Counter1 Capture Event
.ORG OC1Aaddr
RETI ; (TIMER1 COMPA) Timer/Counter1 Compare Match A
.ORG OC1Baddr
RETI ; (TIMER1 COMPB) Timer/Counter1 Compare Match B
.ORG OVF1addr
RETI ; (TIMER1 OVF) Timer/Counter1 Overflow
.ORG OVF0addr
RETI ; (TIMER0 OVF) Timer/Counter0 Overflow
.ORG SPIaddr
RETI ; (SPI,STC) Serial Transfer Complete
.ORG URXCaddr
RETI ; (USART,RXC) USART, Rx Complete
.ORG UDREaddr
RETI ; (USART,UDRE) USART Data Register Empty
.ORG UTXCaddr
RETI ; (USART,TXC) USART, Tx Complete
.ORG ADCCaddr
RETI ; (ADC) ADC Conversion Complete
.ORG ERDYaddr
RETI ; (EE_RDY) EEPROM Ready
.ORG ACIaddr
RETI ; (ANA_COMP) Analog Comparator
.ORG TWIaddr
RETI ; (TWI) 2-wire Serial Interface
.ORG SPMRaddr
RETI ; (SPM_RDY) Store Program Memory Ready
Reset:
LDI temp,Low(RAMEND)
OUT SPL,temp
LDI temp,High(RAMEND)
OUT SPH,temp
SEI
LDI temp,3
OUT MCUCR,temp
LDI temp,(1<<INT0)
OUT GICR,temp
LDI temp,0xFF
OUT DDRB,temp
Start:
LDI temp,0x01
OUT PortB,temp
M1:
rjmp m1
button:
CPI temp,0x80
BREQ m2
LSL temp
OUT PortB,temp
LDI temp1,0
Delay:
DEC temp1
BRNE Delay
Delay1:
DEC temp1
BRNE Delay1
Delay2:
DEC temp1
BRNE Delay2
Delay3:
DEC temp1
BRNE Delay3
Delay4:
DEC temp1
BRNE Delay4
Delay5:
DEC temp1
BRNE Delay5
Delay6:
DEC temp1
BRNE Delay6
Delay7:
DEC temp1
BRNE Delay7
Delay8:
DEC temp1
BRNE Delay8
Delay9:
DEC temp1
BRNE Delay9
Delay10:
DEC temp1
BRNE Delay10 ;тут я пробовал побороть программно, не помогло
RETI
M2:
LDI temp,0x01
OUT PortB,temp
BCLR 1
RETI
; EEPROM ======
.ESEG ;EEPROM
Re: ATmega8A учусь использовать прерывания
Добавлено: Ср янв 21, 2015 08:02:17
sashavir
Интересные глюки вываливаются, справился вроде программной задержкой, но метод конечно, как стрельба из дробовика по воробьям, да еще и крайне ненадежный, дребезжание плавает в зависимости от времени нажатия, а если еще нажимать быстро, то вообще труба... Буду изучать вопрос далее...
1 MHz
Спойлер
Код: Выделить всё
.include "c:\avr\project\m8adef.inc"
.def temp=r16
.def temp1=r17
.def temp2=r18
.def temp3=r19
;= Start macro.inc ==============
;= End macro.inc ================
; RAM =========
.DSEG
; FLASH =======
.CSEG
.ORG 0 ; (RESET)
RJMP Reset
.ORG INT0addr
RJMP button ; (INT0) External Interrupt Request 0
.ORG INT1addr
RETI ; (INT1) External Interrupt Request 1
.ORG OC2addr
RETI ; (TIMER2 COMP) Timer/Counter2 Compare Match
.ORG OVF2addr
RETI ; (TIMER2 OVF) Timer/Counter2 Overflow
.ORG ICP1addr
RETI ; (TIMER1 CAPT) Timer/Counter1 Capture Event
.ORG OC1Aaddr
RETI ; (TIMER1 COMPA) Timer/Counter1 Compare Match A
.ORG OC1Baddr
RETI ; (TIMER1 COMPB) Timer/Counter1 Compare Match B
.ORG OVF1addr
RETI ; (TIMER1 OVF) Timer/Counter1 Overflow
.ORG OVF0addr
RETI ; (TIMER0 OVF) Timer/Counter0 Overflow
.ORG SPIaddr
RETI ; (SPI,STC) Serial Transfer Complete
.ORG URXCaddr
RETI ; (USART,RXC) USART, Rx Complete
.ORG UDREaddr
RETI ; (USART,UDRE) USART Data Register Empty
.ORG UTXCaddr
RETI ; (USART,TXC) USART, Tx Complete
.ORG ADCCaddr
RETI ; (ADC) ADC Conversion Complete
.ORG ERDYaddr
RETI ; (EE_RDY) EEPROM Ready
.ORG ACIaddr
RETI ; (ANA_COMP) Analog Comparator
.ORG TWIaddr
RETI ; (TWI) 2-wire Serial Interface
.ORG SPMRaddr
RETI ; (SPM_RDY) Store Program Memory Ready
Reset:
LDI temp,Low(RAMEND)
OUT SPL,temp
LDI temp,High(RAMEND)
OUT SPH,temp
SEI
LDI temp,3
OUT MCUCR,temp
LDI temp,(1<<INT0)
OUT GICR,temp
LDI temp,0xFF
OUT DDRB,temp
Start:
LDI temp,0x01
OUT PortB,temp
M1:
rjmp m1
button:
CPI temp,0x80
BREQ m2
LSL temp
OUT PortB,temp
LDI temp1,0
LDI temp2,0
LDI temp3,4
Delay:
DEC temp1
BRNE Delay
DEC temp2
BRNE Delay
DEC temp3
BRNE Delay
RETI
M2:
LDI temp,0x01
OUT PortB,temp
BCLR 1
RETI
; EEPROM ======
.ESEG ;EEPROM
Re: ATmega8A учусь использовать прерывания
Добавлено: Ср янв 21, 2015 14:10:26
akl
А так,в предположении, что кнопка стоит между общим и PD2
Спойлер
Код: Выделить всё
.include "m8def.inc"
.CSEG
.ORG 0 ; (RESET)
RJMP Reset
.ORG INT0addr
button:
CBI PORTD,2
SBI DDRD,2 ; заблокировать кнопку выводом 0
IN R16,PORTB
ROL R16
BRCC PC+2
SBR R16,$01
OUT PORTB,R16
SET
RET
Reset:
LDI R16,Low(RAMEND)
OUT SPL,R16
LDI R16,High(RAMEND)
OUT SPH,R16
SBI DDRD,2
SBI PORTD,2
CBI DDRD,2
LDI R16,2
OUT MCUCR,R16
LDI R22,0xFF
OUT DDRB,R22
LDI R22,0x01
OUT PortB,R22
Start:
LDI R22,(1<<INT0)
OUT GICR,R22
OUT GIFR,R22
CLT
SEI
M1:
BRTC M1
RCALL DELAY
CBI DDRD,2
SBI PORTD,2 ;разблокировать кнопку
WAIT:
RCALL DELAY
SBIS PIND,2 ; кнопка ещё нажата
RJMP WAIT ; да, подождём
RCALL DELAY
SBIS PIND,2 ; а может это был дредезг при отпускании?
RJMP WAIT ; да, это дребезг
rjmp START ; наконец-то кнопка отпущена
DELAY:
LDI XH,20
CLR XL
SBIW XL,1
BRNE PC-1
RET
.EXIT