Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Прерывания в Mega32
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Чип-Хрум
Накопились вопросы по программированию прерываний на си.
1. Может ли находится основная программа в прерывании в плоть до вывода на жк экран
к примеру таймера 1 ? На сколко она может быть большой ?
2. Можно-ли прерывание осуществлять из прерывания (из таймера делать вызов ацп).
3. Может ли быть подпрограмма в прерывании, и сколько их может быть внутрь(матрешки),
Короче говоря на что расчитан стек он же не безграничен.
4. Насколько я понял прерывания эта функция с входными и выходными переменными типа void.
То бишь с переменными работать не придется?А как же тогда работать с переменными?
mempfis_
2. "Можно-ли прерывание осуществлять из прерывания (из таймера делать вызов ацп)."
Можно, если уверен, что период прерываний от таймера будет больше, чем время, необходимое для запуска АЦП+время на преобразование+время на обработку прерывания от АЦП+время на сохранение или обработку результатов преобразования. Правда если останавливать ТС или маскировать его прерывание, то это время можно не учитывать.

3. "Может ли быть подпрограмма в прерывании, и сколько их может быть внутрь(матрешки),
Короче говоря на что расчитан стек он же не безграничен."
Подпрограмма в прерывании может быть.
Кол-во зависит от того, сколько входных данных использует подпрограмма и от их типа.

4. "Насколько я понял прерывания эта функция с входными и выходными переменными типа void.
То бишь с переменными работать не придется?А как же тогда работать с переменными?"
Я, например, объявляю переменные так: volatile char (или int, или long int, или bit) variable_name
и в любом месте программы получаю к ней доступ (даже в прерывании).

[/quote]
defunct
Цитата
То бишь с переменными работать не придется?

Интересный вывод.

main() тоже можно описать как void main(void),
разве это как-то помешает работать с переменными?


Цитата
Может ли быть подпрограмма в прерывании, и сколько их может быть внутрь(матрешки),

Конечно.
Количество зависит от объема стека выделенного вами.. для m32 легко можно отвести 512b-1kb под стек.


Цитата
Может ли находится основная программа в прерывании в плоть до вывода на жк экран
к примеру таймера 1 ?

В прерывании или не в прерывании не имеет особого значения.
Значение имеет то, сможет ли конкретный подход обеспечить выполнение всех задач поставленных в ТЗ.
Если основная функция программы выполняется в прерывании (где другие прерывания запрещены), то в этой функции некоторые действия придется решать путем программного опроса (например нельзя будет организовать одновременный прием/передачу по уарту, т.к. в режиме опроса надо проверять флаги состояния; нельзя будет одновременно работать с несколькими периферийными устройствами).. Если вас это устраивает, и программа не сильно сложная - то тогда безусловно можно.

Прерывания организованы для того чтобы сделать возможным обработку множества устройств в самый подходящий для этого момент. В вашем случае процессор что-то считает, выводит данные на LCD. Вдруг в какой-то момент времени АЦП заканчивает преобразование - происходит прерывание, процессор переходит на ПП обслуживания АЦП - вычитывает данные, запускает следующее преобразование и возвращается к прерванной задаче вывода данных на LCD.. В какой-то момент времени приходит символ по УАРТ. Процессор - отвлекается от текущей задачи - вынимает символ пришедший на УАРТ ложит его в очередь и возвращается к выводу данных на LCD.. В момент когда процессор закончит вывод на LCD и ему понадобятся данные с АЦП или с УАРТа, ему уже не придется ждать этих данных, надо будет просто взять их из очереди и обрабатывать.

Так что на мой взгляд не стоит размещать все в прерывании.
ReAl
Цитата(Чип-Хрум @ May 26 2007, 16:29) *
Накопились вопросы по программированию прерываний на си.
1. Может ли находится основная программа в прерывании в плоть до вывода на жк экран
к примеру таймера 1 ? На сколко она может быть большой ?

Не стоит так делать. Лучше в прерывании таймера запустить АЦП и выйти из прерывания, в прерывании АЦП занести значение АЦП в какой-то буфер (максимум - прямо в прерывании обсчитать что-то в духе экспоненциального фильтра, но не дольше) и выставить флаг "есть новое значение", а вещи типа преобразования результата в ASCII и вывода на ЖКИ - лучше делать в фоне (в "основном цикле").
Прерывания хороши, когда их много мелких на короткие "оперативные" задачи, а всё медленное надо вынести наверх. Меньше проблем будет позже, при добавлении других прерываний (UART, другие таймера, внешние источники).
Конечно, можно в таймерном прерывании разрешить вложенные прерывания и программно ждать завершения работы АЦП, конвертить, выводить на экран - но это нехорошо. Наворачивается вложенность, растут стеки. Просто некрасиво.

Цитата(Чип-Хрум @ May 26 2007, 16:29) *
2. Можно-ли прерывание осуществлять из прерывания (из таймера делать вызов ацп).

Запустить АЦП можно, но вложенным прерывание станет только если в таймерном после этого запуска задержаться до тех пор, пока АЦП отработает. Но даже в этом случае я бы вышел из таймерного (пусть АЦП подождёт до этого момента) и вошёл в АЦП-шное отдельно.
Вложенные прерывания хороши при нормальной приоритетной системе, когда можно расписать что важнее чего и что чем может прерываться.

Цитата(Чип-Хрум @ May 26 2007, 16:29) *
3. Может ли быть подпрограмма в прерывании, и сколько их может быть внутрь(матрешки),
Короче говоря на что расчитан стек он же не безграничен.

Сколько есть памяти. Иногда это (сделать в прерывании что-то срочное, разрешить вложенные прерывания и продолжать потихоньку делать медленную часть) действительно удобно, но только иногда. И если тяжело при разрешении вложенных указать - какие могут "вкладываться", а какие - нет, то область "иногда" сокращается.

Цитата(Чип-Хрум @ May 26 2007, 16:29) *
4. Насколько я понял прерывания эта функция с входными и выходными переменными типа void.
То бишь с переменными работать не придется?А как же тогда работать с переменными?

Глобальные, как это не прискорбно :-)
Могут быть переменные уровня файла и доступ к ним через специальные функции (запрещающие на время доступа все или только нужные прерывания), могут быть "общедоступные" - тогда обязательно volatile и всё равно иногда придётся запрещать прерывания при доступе к ним. volatile гарантирует невыбрасывание обращений при оптимизации, но не гарантирует атомарности обращений к многобайтовым переменным.
Spider
Ребят, а что будет если у меня например работает прерывание по UART_RECV и в этот момент сработало прерывание по таймеру? Короче, что будет если во время одного прерывания возникло второе?
И как управлять стеком в WinAVR/AVRStudio?
IgorKossak
Цитата(Alexey Belyaev @ Jun 4 2007, 08:49) *
Ребят, а что будет если у меня например работает прерывание по UART_RECV и в этот момент сработало прерывание по таймеру? Короче, что будет если во время одного прерывания возникло второе?

Два варианта:
1. Если в теле UART_RECV разрешить прерывания, то прерывание таймера выполнится сразу, а потом управление перейдёт обратно в UART_RECV с прерванного места.
2. Если в UART_RECV не разрешать прерываний, то прерывание от таймера выполнится после выхода из UART_RECV.
Цитата(Alexey Belyaev @ Jun 4 2007, 08:49) *
И как управлять стеком в WinAVR/AVRStudio?

Что значит управлять? Размер и расположение определяется в скрипте линкера, запускаемом обычно из make.
Работу же со стеком осуществляет сам компилятор независимо от пользователя.
ALexx
Цитата(Alexey Belyaev @ Jun 4 2007, 08:49) *
Ребят, а что будет если у меня например работает прерывание по UART_RECV и в этот момент сработало прерывание по таймеру? Короче, что будет если во время одного прерывания возникло второе?


Во время отработки текущего прерывания все остальные по умолчанию запрещены:

When an interrupt occurs, the Global Interrupt Enable I-bit is cleared and all interrupts
are disabled.

Т.е. "спокойно" отрабатывается текущее прерывание, выходим из обработчика. и , если есть новый источник прерывания, переходим к его обработке.

Однако, есть возможность организовать вложенные прерывания:

The user software can write logic one to the I-bit to enable nested interrupts.
All enabled interrupts can then interrupt the current interrupt routine.

Т.е. в данном случае вы должны "ручками" установить бит I.
Чип-Хрум
Вот пример программы в ней переменная unsigned char adc_end при возврате из прерывания
в подфункцию ADC_2 изменяет значение с 0 на 2 .
Кто знает как это исправить подскажите.
Да простят меня модераторы но мой файл с расширением .с ваш сайт брать не хочет и
пишет вот это "Ошибка загрузки. У Вас нет прав для загрузки файла с таким расширением."

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include <util/delay.h>
#include <stdio.h>

volatile unsigned int cond1=0;
volatile unsigned int adc_col=0;
volatile unsigned char adc_end=0;
volatile unsigned char adcl=0;

unsigned char ADC_2(unsigned char adc_end);

ISR(ADC_vect)
{
adc_col++;

if (adc_col>3)
{

adcl=ADCL;
cond1=ADCH;
cond1=cond1<<8;
cond1=cond1+adcl;

ADMUX=0x40;
ADCSRA=0xe7;
adc_end=0; //при возврате из этой точки переменная изменяет свое значение
adc_col=0;

}
else
{

ADMUX=0x41;
ADCSRA=0xcf;
}
}


int main(void)
{

sei();

DDRA=0x00;
PORTA=0x00;

DDRD=0xff;

DDRB=0xb0;
PORTB=0x00;

DDRC=0xff;
PORTC=0x00;


SFIOR=0x00;

adc_end = 2;

ADC_2(adc_end);

//??????????? ?????????

return (0);
}

unsigned char ADC_2(unsigned char adc_end)
{

ADMUX=0x41;
ADCSRA=0xcf;

while (adc_end>1)
{
asm volatile ("nop " "\n\t"
"nop " "\n\t"
"nop " "\n\t"
"nop " "\n\t"
"nop " "\n\t"
"nop " "\n\t"
"nop " "\n\t"
"nop " "\n\t"
"nop " "\n\t"
"nop " "\n\t"
"nop " "\n\t"
"nop " "\n\t"
:smile.gif;
}

return adc_end;

}
mdmitry
Цитата(Чип-Хрум @ Jun 9 2007, 13:37) *
unsigned char ADC_2(unsigned char adc_end);

int main(void)
{

sei();

DDRA=0x00;
PORTA=0x00;

DDRD=0xff;

DDRB=0xb0;
PORTB=0x00;

DDRC=0xff;
PORTC=0x00;


SFIOR=0x00;

adc_end = 2;

ADC_2(adc_end);

//??????????? ?????????

return (0);
}


В main нет рабочего цикла: действия выполняются последовательно
>adc_end = 2;
>ADC_2(adc_end);
а прерывание было во время выполнения main?
Если нет, то все верно. Никаких изменений для adc_end.
Если было, то когда?
Если до команды:
adc_end = 2;
то все правильно, т.к. присваивание после прерывания, а если после, то надо разбираться подробно.
xemul
Цитата(Чип-Хрум @ Jun 9 2007, 13:37) *
Вот пример программы в ней переменная unsigned char adc_end при возврате из прерывания
в подфункцию ADC_2 изменяет значение с 0 на 2 .
Кто знает как это исправить подскажите.

volatile unsigned char adc_end=0;
unsigned char ADC_2(unsigned char adc_end)
{
}

С точки зрения компилятора глобальная переменная "volatile unsigned char adc_end=0;" и аргумент функции ADC_2 "unsigned char adc_end" никакого отношения друг к другу не имеют.
В прототипах функций рекомендуется указывать только типы аргументов, а не имена.
Стиль программы комментировать не буду, но предлагаю подумать:
1) куда программа вернется из main()
2) о целесообразности ожидания окончания преобразования АЦП в какой-то п/п в тупом цикле (в предположении, конечно, что программа должна делать что-то еще, и main() все-таки организован наподобие:
Код
void main(void)
{
...
   for(;;)
   {
   ...
   }
}

)
Цитата
Да простят меня модераторы но мой файл с расширением .с ваш сайт брать не хочет и
пишет вот это "Ошибка загрузки. У Вас нет прав для загрузки файла с таким расширением."

А рулесы почитать? Аттачить можно zip, rar, txt, pdf, gif, jpg.
Чип-Хрум
Ув. xemul я спрашивал конкретно а ты не ответил , я дал пример на катором наглядно
переменная adc_end изменяет свое значение там где не должна этого делать.
[SENSORED] возьми и откомпелируй прогррамку поставь брейкпойнт
на то место где я тебе предлагаю и запусти отладчик . А потом Подывысь як цыферки то
изменяются.
То что ты предложил изменить прототип я изменил не помогло!
defunct
Чип-Хрум

Формулируйте вопрос нормально.
что где чего изменяет?

В main() у вас
adc_end = 2;
zltigo
Цитата(xemul @ Jun 9 2007, 13:24) *
В прототипах функций рекомендуется указывать только типы аргументов, а не имена.

Совершенно неразумная "рекомендация", поскольку имя переменной может и является подсказкой-комменарием и совершенно ничему не мешает. Прототипы стиля function( int, int, int );
по сравнению с function( int size, int mask, int start_value); теряют массу полезной для человека информации.





Цитата(Чип-Хрум @ Jun 9 2007, 19:13) *
я спрашивал ...

Настоятельно рекомендую повысить уровень грамотности своих сообщений.
Правила:
Цитата
2.1.в. Высказываться понятно, полно и грамматически правильно...
.... в противном случае пост может быть расценен как текстовый мусор (флуд).
SunnyDevil
Чтобы понять почему написанная программа та себя ведет вернемся к определению "контекста". В контескте обработчика прерывания adc_end это глобальная переменная. В контесте main эта переменная также глобальная. А вот в контесте той функции в конце эта переменная локальная просто с темже именем. Соверешенно естетственно что прерывание, меняя содержимое глобальной переменной никоим образом не касается переменной локальной внутри этой функции и в debug'e вы наблюдаете якобы изменение переменной.Но это не значение переменной меняется, а выводится другая переменная с другим значением просто имена у них совпадают. Минимальные изменения будут:
unsigned char ADC_2(unsigned char &adc_end);
Либо
unsigned char ADC_2(unsigned char *adc_end);

Естественно с небольшими правками кода вызова и кода внутри этой функции.
xemul
Цитата(Чип-Хрум @ Jun 9 2007, 20:13) *
Ув. xemul я спрашивал конкретно а ты не ответил , я дал пример на катором наглядно
переменная adc_end изменяет свое значение там где не должна этого делать.

Ошибаетесь, я ответил. Вы, вероятно, невнимательно читаете.
Код
С точки зрения компилятора глобальная переменная "volatile unsigned char adc_end=0;" и аргумент функции ADC_2 "unsigned char adc_end" никакого отношения друг к другу не имеют.

Переведу с русского на русский. Вы передаете в функцию ADC_2 один аргумент adc_end по значению, и оно случайно оказывается равно значению глобальной переменной adc_end. То, что имя аргумента и имя глобальной переменной совпадают, не обязывает компилятор С считать их одним и тем же - это не Васик. Как следствие, в прерывании исправно модифицируется значение глобальной переменной adc_end, что никак не отражается на состоянии локальной переменной adc_end функции ADC_2().
Цитата
[SENSORED] возьми и откомпелируй прогррамку поставь брейкпойнт
на то место где я тебе предлагаю и запусти отладчик . А потом Подывысь як цыферки то
изменяются.
То что ты предложил изменить прототип я изменил не помогло!

Ваша программа не требует для разбора проблемы таких усилий. Кста, если место для брейкпоинта означено //???..., то при заданных начальных условиях исполнение программы до него никогда не дойдет.
Я предложил не изменить прототип, а подумать.
Попробуйте следовать в своих постах элементарным этическим нормам, местами распространным в стране моего проживания, или хотя бы правилам форума.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.