Про прерывания в АВР
Про прерывания в АВР
Имеется програмка, написаная чтобы понять процесс обработки прерывания с внешнего входа ( INT_0 ) АВР-а 2313. При работе программы почему то не хочет срабатывать обработчик прерывания по INT_0 . Регистры GIMSK и MCUCR вроде бы настроены правильно ( разрешение INT_0 и срабатывание по низкому уровню), а при подаче сигнала "0" на вход INT_0 основная программа останавливается и всё. При снятии сигнала, основная программа продолжает своё выполнение. А где же обработка прерывания? Помогите разобраться пожалуйста.
- Вложения
-
- code.asm
- Программа мигает светодиодами, а при внешнем прерывании по-идее должна остановить мигания и зажечь все светодиоды порта В. Но чего то не хочет
- (1.48 КБ) 603 скачивания
Re: Про прерывания в АВР
Рома1001 писал(а):Имеется програмка, написаная чтобы понять процесс обработки прерывания с внешнего входа ( INT_0 ) АВР-а 2313. При работе программы почему то не хочет срабатывать обработчик прерывания по INT_0 . Регистры GIMSK и MCUCR вроде бы настроены правильно ( разрешение INT_0 и срабатывание по низкому уровню), а при подаче сигнала "0" на вход INT_0 основная программа останавливается и всё. При снятии сигнала, основная программа продолжает своё выполнение. А где же обработка прерывания? Помогите разобраться пожалуйста.
Да уж, по-моему легче исправить чем объяснить
Код: Выделить всё
.include "D:\avr\avrasm\APPNOTES\2313DEF.INC"
.def Temp=R16
.def Temp1=R17
.def Temp2=R18
.def Temp3=R19
.org 0
rjmp RESET ; при сбросе микроконтроллер стартует с 0 адреса,
; вот с этого адреса (явно, директивой орг его указываем)
; и начинается выполнение программы
; далее следует таблица прерываний ее тоже желательно заполнять с
;использованием директивы орг
; т.к. мне лень, то я заполню только прерывания инт0,
; остальные если не используются можно не заполнять но правильнее
; по каждому адресу неисп прерывания разместить команду reti (см. ниже)
.org INT0addr ; по адерсу перехода на прерывание инт0 располагаем
; команду перехода на собственно обработчик прерывания
rjmp INT_0;
.org INT1addr ; по адерсу перехода на прерывание инт1 располагаем
; команду возврата из прерывания, если оно случайно выполнится;
reti ; то программа просто вернется из него, хотя случайное выполнение
; происходить не должно
RESET:
ldi Temp,RamEnd ; после сброса перво-наперво инициализируем стек
out SPL,Temp
далее программа бла, бла бла ....
INT_0:
ldi r28,$ff
LED: out portB,r28 ; а вот этот кусок заглючит программу насмерть
rjmp LED ; т. е. выполняется безусловный переход на метку
reti
.....и еще, имейте ввиду, что прерывание по уровню защелкивается столь долго, сколь долго на нужной ноге присутствует 0, поэтому пока вы не снимете 0 процедура обработки прерывания будет выполняться снова и снова, (выполняея по одной команде основной программы после каждого выполнения процедуры)
- tych
- Э...
- Сообщения: 2792
- Зарегистрирован: Ср апр 04, 2007 08:39:14
- Откуда: Москва
- Контактная информация:
Re: Про прерывания в АВР
Рома1001 писал(а): А где же обработка прерывания? Помогите разобраться пожалуйста.
обработка происходит в специальном участке кода называемом - обработчик прерывания. Его должен создать программист не использующий мастер создания кода.
Думайте сами, решайте сами ... а вот он-лайн перевод на корявый русский http://translate.ru
а вот этот кусок заглючит программу насмерть
Разобрался. Большое спасибо за помощь. Я понял-просто напросто смешал всё в кучу, а надо было разложить по полочкам. Насчёт куска который заглючит программу насмерть-не в нём дело, это написано так, для проверки, я ж разбирался с прерываниями. Про "пока вы не снимете 0 процедура обработки прерывания будет выполняться снова и снова" я в курсе, но за напоминание спасибо-мож кто не знает из читателей форума.
А вот интересно, обработчик в обработчике может находиться? Например
INT_0:
......
.......
Timer1_comp1:
.......
.......
reti
reti
А вот интересно, обработчик в обработчике может находиться? Например
INT_0:
......
.......
Timer1_comp1:
.......
.......
reti
reti
- ARV
- Ум, честь и совесть. И скромность.
- Сообщения: 18544
- Зарегистрирован: Чт дек 28, 2006 08:19:56
- Откуда: Новочеркасск
- Контактная информация:
во-первых, каков смысл нохождения одного обработчика в другом? с точки зрения программы - возможно все, с точки зрения здравого смысла - сомнительно, что это необходимо...
во-вторых, при любых раскладах последняя команда reti - лишняя
во-вторых, при любых раскладах последняя команда reti - лишняя
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
MetEl писал(а):smac вам бы статьи писать, правда по серёзней и ещё более раскрыто(мнемоника комманд).
Шутку понял, смешно. (с)
Да будет, гнев модераторов, не столь страшен, короче сорри за офтоп
А если серьезно, то стати это очень тяжелая вещь, ни сил, ни времени, ни желания нет. Возможно пока нет.
ARV писал(а):во-первых, каков смысл нохождения одного обработчика в другом? с точки зрения программы - возможно все, с точки зрения здравого смысла - сомнительно, что это необходимо...
во-вторых, при любых раскладах последняя команда reti - лишняя
Ой, прошу прощения, не это хотел спросить. В обработчеке INT_0 хочу разместить инициализацию таймера, у которого свои прерывания. Будут ли они обрабатываться пока происходит процесс обработки прерывания по INT_0 ? Ведь в SREG бит I сбрасывается, когда произошло прерывание и запрещает любые другие. Тогда получается надо его установить что ли? Не помешает ли это прерыванию по INT_0?
Рома1001 писал(а):Ой, прошу прощения, не это хотел спросить. В обработчеке INT_0 хочу разместить инициализацию таймера, у которого свои прерывания. Будут ли они обрабатываться пока происходит процесс обработки прерывания по INT_0 ? Ведь в SREG бит I сбрасывается, когда произошло прерывание и запрещает любые другие. Тогда получается надо его установить что ли? Не помешает ли это прерыванию по INT_0?
Да, правильно, необходимо установить бит I, однако, нужно позаботиться чтобы не произошел повторный вызов обработчика прерывания из самого себя. В данном случае, в обработчике прерываний инт0 нужно запретить собственно прерывания от инт0, перед тем как глобально разрешенить прерывания. Ну и естественно нужно придумать механизм разрешения прерываний от инт0 для последующих вызовов.
А вообще следует избегать подобных ситуаций (прерывания прерываний), для чего нужно писать обработчики прерывания максимально быстрыми, не использовать в них задержки, сложные расчеты и др. медленные вещи
В данном случае, в обработчике прерываний инт0 нужно
"В данном случае, в обработчике прерываний инт0 нужно запретить собственно прерывания от инт0, перед тем как глобально разрешенить прерывания"- вы имеете ввиду регистр GIMSK? Ведь глобально я разрешаю прерывания в основной программе командой SEI. А так всё немного прояснилось, будем думать, спасибо 
Re: В данном случае, в обработчике прерываний инт0 нужно
Рома1001 писал(а):"В данном случае, в обработчике прерываний инт0 нужно запретить собственно прерывания от инт0, перед тем как глобально разрешенить прерывания"- вы имеете ввиду регистр GIMSK? Ведь глобально я разрешаю прерывания в основной программе командой SEI. А так всё немного прояснилось, будем думать, спасибо
под глобальным рарешением прерываний я имею ввиду именно команду sei, которая собственно и устанавливает флаг I в регистре состояния. При входе в прерывания флаг I сбрасывается автоматически, что эквивалентно команде cli. Значит вам следует поступить следующим образом:при входе в прерывание от инт0 в регистре GIMSK тследует сбросить флаг INT0, тем самым запретив вызов прерываний от инт0. Далее следует разрешить прерывания глобально командой sei, таким образом, если прерывания от таймера будут разрешены и наступит условие прерываний, то они (таймерные преырвания) прервут обработчик прерывания от инт0. Далее выполнится обработчик прерывания таймера, после чего программа вернется на обработчик инт0. Перед выходом из инт0 следует запретить глобальные прерывания (cli) затем разрешить прерыания от инт0 (если они все еще нужны).
Так и я про то же, только вы меня наверно не правильно поняли. Я имел ввиду во так:
INT_0:
ldi Temp,0
out gimsk,Temp ;запретили прерывания по INT0
ldi Temp,0b10000000
out SREG,Temp ;разрешили дальнейшие прерывания
.....
.....
..... ; текст программы с таймером например
ldi Temp,0b01000000
out gimsk,Temp ;разрешили прерывания по INT0 для дальнейших целей
Ну примерно. А не CLI
И ещё , текст обработчика прерываний от таймера ( как в тексте выше) писать где то отдельно в конце программы или в рамках обработчика прерываний от INT0?
INT_0:
ldi Temp,0
out gimsk,Temp ;запретили прерывания по INT0
ldi Temp,0b10000000
out SREG,Temp ;разрешили дальнейшие прерывания
.....
.....
..... ; текст программы с таймером например
ldi Temp,0b01000000
out gimsk,Temp ;разрешили прерывания по INT0 для дальнейших целей
Ну примерно. А не CLI
И ещё , текст обработчика прерываний от таймера ( как в тексте выше) писать где то отдельно в конце программы или в рамках обработчика прерываний от INT0?
Рома1001 писал(а):Так и я про то же, только вы меня наверно не правильно поняли. Я имел ввиду во так:
INT_0:
ldi Temp,0
out gimsk,Temp ;запретили прерывания по INT0
ldi Temp,0b10000000
out SREG,Temp ;разрешили дальнейшие прерывания
.....
.....
..... ; текст программы с таймером например
ldi Temp,0b01000000
out gimsk,Temp ;разрешили прерывания по INT0 для дальнейших целей
Ну примерно. А не CLI
И ещё , текст обработчика прерываний от таймера ( как в тексте выше) писать где то отдельно в конце программы или в рамках обработчика прерываний от INT0?
Код: Выделить всё
ldi Temp,0b10000000
out SREG,Temp ;разрешили дальнейшие прерыванияв принципе верно, но лучше одной командой - sei
Код: Выделить всё
ldi Temp,0b01000000
out gimsk,Temp ;разрешили прерывания по INT0 для дальнейших целейперед этим куском необходимо вставить cli, если этого не сделать то в вашем случае (инт0 - прерывания по низкому уровню сигнала) прерывания от инт0 вызовутся после выполнения команды следующей за out gimsk,Temp. т. е. обработчик прерываний вызовется сам из себя. Корректней так:
Код: Выделить всё
cli : запретили прерывания глобально, чтобы избежать ненужного вызова обработчика
ldi Temp,0b01000000
out gimsk,Temp ;разрешили прерывания по INT0 для дальнейших целейЕстественно обработчик прерываний таймера нужно писать отдельно, т. е. не в обработчике прерываний инт0, и также необходимо поместить в векторе прерываний переход на обработчик прерываний таймера
Re: В данном случае, в обработчике прерываний инт0 нужно
smac писал(а):***таким образом, если прерывания от таймера будут разрешены и наступит условие прерываний, то они (таймерные преырвания) прервут обработчик прерывания от инт0.
В этом нет ничего страшного, и даже совсем ничего.
Эт нормально, ведь есть стек.
Просто нужно по времени рассчитать (учитывать) чтобы хватило и на обработчик счетчика и внешнего запроса, в постоянном цикле, с максимальными затратами(временем обработок).
Можно даже в одном прерывании ждать другого. Устанавливая(резервируя) величину стека (выделяемую компилятором, подустим).
простое чмо, выдумщик
Re: В данном случае, в обработчике прерываний инт0 нужно
MetEl писал(а):В этом нет ничего страшного, и даже совсем ничего.
Эт нормально, ведь есть стек.
...
Можно даже в одном прерывании ждать другого. Устанавливая(резервируя) величину стека (выделяемую компилятором, подустим).
Да, ничего страшного, это возможно и даже не запрещено. Просто мало толку от прерывания которое выполняется очень долго. Тем более совсем бестолково, когда прерывание ждет другого прерыания - работа стоит а срок идет. Т. е. в это время контроллер "молотит" попросту
- sachok
- Опытный кот
- Сообщения: 849
- Зарегистрирован: Сб янв 05, 2008 11:05:15
- Откуда: Україна м.Луцьк
- Контактная информация:
Что бы не создавать новую тему напишу сдесь:) Проверьте правильность настройки прерываний таймера0. Вот код:
1 раз в 1 секунду должен переполнится таймер0 и выдать прерывание при котором на PORTВ.0 должна быть лог.1. Всё ли сдесь правильно?
Код: Выделить всё
#include <mega8>
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
// Reinitialize Timer 0 value
TCNT0=0xFF;
// Place your code here
PORTB.0=1;
}
void main(void)
{
PORTB=0x00;
DDRB=0x01;
PORTC=0x00;
DDRC=0x00;
PORTD=0x00;
DDRD=0x00;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 4000,000 kHz
TCCR0=0x01;
TCNT0=0xFF;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
MCUCR=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x01;
// Global enable interrupts
#asm("sei")
while (1);
}1 раз в 1 секунду должен переполнится таймер0 и выдать прерывание при котором на PORTВ.0 должна быть лог.1. Всё ли сдесь правильно?
Я не Сашок!!!
-
BerZerK-ku
- Мучитель микросхем
- Сообщения: 492
- Зарегистрирован: Вт июл 22, 2008 08:10:54
sachok писал(а):1 раз в 1 секунду должен переполнится таймер0 и выдать прерывание при котором на PORTВ.0 должна быть лог.1. Всё ли сдесь правильно?
Неправильно.
Что у вас получится:
TCCR0=1 -> нет делителя частоты -> частота срабатывания таймера 0 4Мгц, т.е. 1 шаг = 0.25мкс
Прерывание используете по переполнению, в счетчике изначально FF. Значит прерывание у вас будет вызываться раз в 0.25мкс. Вообщем чушь.
Таймер 0, при тактовой частоте в 4Мгц, не получится настроить на 1с.
Надо либо настраивать его срабатывание на меньшее время и отталкиваться уже от него, либо использовать 16-и разрядный таймер1.
TCCR1B=4; // делитель 256
TCNT1=65535-15625+1; // нужны 15625 отсчетов
-
BerZerK-ku
- Мучитель микросхем
- Сообщения: 492
- Зарегистрирован: Вт июл 22, 2008 08:10:54
Если остальные таймера постоянно работают в одном режиме. то можно воспользоваться ими.
Иначе, надо организовать счетчик.
Т.к. точность не важна, то можно так:
Иначе, надо организовать счетчик.
Т.к. точность не важна, то можно так:
Код: Выделить всё
int Count=0; // счетчик времени (int с запасом на минуты)
char 1s=0; //флаг, 1сек
interrupt [TIM0_OVF] void timer0_ovf_isr(void){
// Reinitialize Timer 0 value
TCNT0=256-195+1;
iCount++;
if (iCount>=20){ //20*0.05с = 1с
iCount=0;
1s=1;
}
}
void main(void){
...
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 4000,000 kHz
TCCR0=0x05; //делитель 1024
TCNT0=256-195-1; // примерно раз в 0.05с
...
while(1){
if (1s) {
1s=0;
//обработчик
PORTB.0=1;
}
}
}