Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Приоритет прерываний
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Страницы: 1, 2
PhX
Есть три обработчика прерываний:
От Timer1
от INT0
и допустим
от Timer2
Прерывание от INT0 наиважнейшее не обработается вовремя расстрел (считает импульсы энкодера максимальная частота 250 КГц). Вопрос как сделать так, чтобы это прерывание имело наивысший приоритет и прерывало обработчики остальных прерываний?
Компилятор WinAVR.
zltigo
Цитата(PhX @ Aug 28 2008, 18:53) *
...как

Поскольку никакой аппаратной поддержки ни вложенности, ни приоритетов у AVR нет, то только руками - при входе в прерывания запретить индивидуально таймерные и затем поднять контроллеру флаг I автоматически сброшенный при входе в обработчик. Не забыть породелать обратную оперрацию при выходе. Это все лишние такты и критические секции, что для 250KHz-64такта не подарок.... Легко можете пролететь.
defunct
Цитата
при входе в прерывания запретить индивидуально таймерные и затем поднять контроллеру флаг I автоматически сброшенный при входе в обработчик.

TIFR очищается автоматически при входе в обработчик.
Если интервалы между таймерными прерываниями достаточно большие, и можно гарантировать, что обработчик успеет выполниться до повторного прерывания от этого же таймера, тогда можно просто взводить флаг I сразу на входе таймерных прерываний и больше ничего не делать.

Также можно попытаться уменьшить латентность обработчиков таймерных прерываний. Если латентность каждого из них в отдельности будет меньше 64 тактов, то гарантированно ни один INT0 не потеряется. Вследствии того, что если будет два необработанных interrupt request'a выиграет INT0.
smac
Цитата(zltigo @ Aug 28 2008, 21:11) *
Поскольку никакой аппаратной поддержки ни вложенности, ни приоритетов у AVR нет, то только руками - при входе в прерывания запретить индивидуально таймерные и затем поднять контроллеру флаг I автоматически сброшенный при входе в обработчик. Не забыть породелать обратную оперрацию при выходе. Это все лишние такты и критические секции, что для 250KHz-64такта не подарок.... Легко можете пролететь.

По-моему, в ваш ответ вкралась некоторая неточность, не сочтите за наглость. Сам подход, конечно единственно правильный, но у автора темы задача прерывать не внешнее INT0 прерывание, а наоборот таймерные, поэтому нужно при входе в таймерные прерывания разрешать прерывание от инт0 вскинуть флаг разрешения общих, а вот в обработчике инт0 ничего делать по идее не нужно. Конечно, при таком подходе есть вероятность "пролететь" таймерные прерывания, во время обработки инт0.
zltigo
Цитата(smac @ Aug 28 2008, 21:28) *
поэтому нужно при входе в таймерные прерывания разрешать прерывание от инт0 вскинуть флаг разрешения общих

INT0 и так разрешено, а запретить нужно таймерные и прочие, кроме INT0, иначе они будут тоже прерывать текущее.


Цитата(defunct @ Aug 28 2008, 21:28) *
Если интервалы между таймерными прерываниями достаточно большие...

Если их несколько, то их частота уже не имеет значения, ибо могут перекрыватся.


Цитата(defunct @ Aug 28 2008, 21:28) *
Если латентность каждого из них в отдельности будет меньше 64 тактов, то гарантированно ни один INT0 не потеряется.

Нескольким подряд INT0 просто вообще хватит времени, если между ними вклинится 64 такта таймера. INT0 потеряется при обработке предыдущего INT0.
defunct
Цитата(zltigo @ Aug 28 2008, 22:40) *
Если их несколько, то их частота уже не имеет значения, ибо могут перекрыватся.

вот и надо считать с учетом что их несколько.
Напр. INT0 занимает 48 тактов на обработку (период - 64 такта) - 75% ресурса проца.
T1 - 100 тактов (период - 10K тактов)
T2 - 200 тактов. (период - 10K тактов)

считаем.
гарантированное время для обработчика T1 (при любых раскладах он получит столько времени до наступления следующего прерывания) - 10K * 0.25 - 200 = 2300 тактов, 2300 > 100 все Ок.
гарантированное время для обработчика T2 соответвенно:
10K * 0.25 - 100 = 2400 (> 200).

Не нравится одинаковый период, попробуем для разных:
INT0 - 48 тактов (75%).
T1 - 100 тактов (период - 10K тактов)
T2 - 200 тактов. (период - 1K тактов)

гарантированное время для обработки T1 = 10K * 0.25 - (10K/1K * 200) = 2500 - 2000 = 500 (> 100).
для T2 без изменений 2400.
И здесь все Ок.

Цитата
Нескольким подряд INT0 просто вообще не хватит времени, если между ними вклинится 64 такта таймера.

хватит если как сказано выше не 64такта таймера, а гарантировано меньше 64-х (напр 63).
zltigo
Цитата(defunct @ Aug 28 2008, 21:47) *
вот и надо считать с учетом что их несколько.

Так считайте. Два с некратными периодами и что там будет при прерывании одного другим.
Цитата
хватит если как сказано выше не 64такта таймера, а гарантировано меньше 64-х (напр 63).

Разумеется нет. Думайте, сколько времени остается обработчику INT0 после Ваших 63 тактов.
smac
Цитата(zltigo @ Aug 28 2008, 23:40) *
INT0 и так разрешено, а запретить нужно таймерные и прочие, кроме INT0, иначе они будут тоже прерывать текущее.

Да, Вы правы, меня бес попутал.

А вообще задача похоже достаточно серьезная, на С, наверное, нужно будет очень глубоко вникнуть в суть кода. Вопрос к автору темы - а нет ли возможности считать импульсы с помощью таймера-счетчика?
SasaVitebsk
Цитата(zltigo @ Aug 28 2008, 22:40) *
INT0 и так разрешено, а запретить нужно таймерные и прочие, кроме INT0, иначе они будут тоже прерывать текущее.

Как правило, таймерные приходят с определённой периодичностью. Периодичность вполне высчитывается. Если их период превышает означенный период (4мс), то достаточно просто разрешить прерывание в таймерных обработчиках. Возможность 3 вложенных - не проблема. Это лишь незначительно увеличит время реакции на прерывание. Хотя здесь надо учитывать объём контекста для этих прерываний (при написании на Си). Важно также чтобы не использовались совместные переменные в прерываниях. Также необходимо увеличить размер стека (что естественно)
Цитата
Если их несколько, то их частота уже не имеет значения, ибо могут перекрыватся.
Нескольким подряд INT0 просто вообще хватит времени, если между ними вклинится 64 такта таймера. INT0 потеряется при обработке предыдущего INT0.

Не совсем понятно. Лишь бы не пришли одинаковые прерывания. То есть необходимо чтобы суммарное время обработки всех прерываний было меньше (с запасом) чем период найменьшего из прерываний.

Есть ещё ограничения. Столкнулся недавно. Нельзя так обрабатывать те прерывания, флаг которых не сбрасывается автоматически при входе в прерывание. Либо надо их запрещать перед разрешением.
Например при обработке USART_UDRE.
defunct
Цитата(zltigo @ Aug 28 2008, 23:02) *
Так считайте. Два с некратными периодами и что там будет при прерывании одного другим.

так посчитал. что неустраивает в расчетах.
И откуда у таймера будет некратный период.

Цитата
Разумеется нет. Думайте, сколько времени остается обработчику INT0 после Ваших 63 тактов.

Очевидно - все оставшееся время. Таймер не сможет прервать INT0.
Самое главное не пропустить ни одного INT, - влететь в обработчик INT0 до того как произойдет следующий INT0. Все остальное фиолетово, при условии что обработка самого INT0 занимает тоже строго меньше 64 тактов.
zltigo
Цитата(SasaVitebsk @ Aug 28 2008, 22:23) *
Это лишь незначительно увеличит время реакции на прерывание.

В этом и проблема, ибо хотят обрабатывать прерывания с частотой в 250KHz тут уже каждый такт для AVR становится значительным.


Цитата(defunct @ Aug 28 2008, 22:35) *
И откуда у таймера будет некратный период.

Я что,не имею права запустить таймер, напимер, на 1000 и 10001 тик? Или любые другие нацело не деляющиеся...
defunct
Цитата(zltigo @ Aug 28 2008, 23:46) *
В этом и проблема, ибо хотят обрабатывать прерывания с частотой в 250KHz тут уже каждый такт для AVR становится значительным.

Именно, но необходимое и достаточное условие для того чтобы не терять события частоты F это:

A ) обрабока события должна происходить строго быстрее 1/F.
B ) Время между возникновением события и входом в обработчик должно быть сторого меньше 1/F.

Вот от этого и плясать.
"A" никак не зависит от обработчиков других прерываний.
"B" зависит от обработчиков других прерываний и может быть достигнуто двумя путями -
1)разрешением прерываний внутри левых обработчиков
2)сокращением латентности обработчиков.

Вот собсно и все. Все это легко считается по крайней мере для AVRки, т.к. кол-во тактов на обработчик известно, приоритеты обработчиков при одновременном возникновении нескольких прерываний - известны.

Цитата
Я что,не имею права запустить таймер, напимер, на 1000 и 10001 тик? Или любые другие нацело не деляющиеся...

Имеете право конечно, но для простоты расчетов мы возмем минимальный период (или макс возможную частоту).
zltigo
Цитата(defunct @ Aug 28 2008, 22:35) *
...при условии что обработка самого INT0 занимает тоже строго меньше 64 тактов.

Нет. третьи сутки идут "расстрельные" прерывания каждое длится по 63 такта. Периодически приходят несколько таймерных по 63 такта. Вопрос, что будет, если
1. таймерные не прерываются.
2.Если таймерные прерываются, но естественно, для программной реализации вложенности и приоритетов требуются критические секции.
3. Что-то мне подсказывает smile.gif что Автор поднял вопрос не уложив программу в несколько тактов smile.gif
defunct
Цитата(zltigo @ Aug 28 2008, 23:54) *
Нет. третьи сутки идут прерывания INT0 каждое длится по 63 такта. Периодически приходят несколько таймерных по 63 такта. Вопрос, что будет, если
1. таймерные не прерываются.

очевидно таймерные будут курить бамбук, т.к. INT0 приоритетнее. Если же появится окно, когда INT0 не будет, тогда вызовется обработка таймера. А поскольку длительность обработчика таймера меньше периода INT0, то во время обработки таймерного прерывания два события INT0 произойти просто не успеют. Т.о. как только обработка таймера завершится мы влетим в INT0 и сбросится соотв. флаг, во время обработки придет следующее событие INT0 - сразу после выхода из обработчика, мы влетим обратно в обработчик INT0.. и т.д. Ни одного события INT0 мы гарантировано не потеряем.

Цитата
2.Если таймерные прерываются, но естественно, для программной реализации вложенности и приоритетов требуются критические секции.

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

Цитата(zltigo @ Aug 28 2008, 23:54) *
3. Что-то мне подсказывает smile.gif что Автор поднял вопрос не уложив программу в несколько тактов smile.gif

Да - много неизвеcтных в его задаче.
Нужно знать как минимум периоды T1, T2 и хотя бы приблизительное время обработки каждого из трех событий. Тогда можно советовать наверняка.
zltigo
Цитата(defunct @ Aug 28 2008, 23:03) *
очевидно таймерные будут курить бамбук, т.к. INT0 приоритетнее.

Поскольку Вы начали строить всевозможные предположения, то я специально в вопросе убрал INT0 smile.gif,
но Вы успели отцировать первоначальный copy-paste вариант Ж(.
Цитата
Требуются, но можно обойтись и без них, если есть гарантия, что повторный вход в этот же обработчик никогда не успеет произойти.

Таких гарантий при нескольких источниках прерывания нет.
defunct
Цитата(zltigo @ Aug 29 2008, 00:14) *
Поскольку Вы начали строить всевозможные предположения, то я специально в вопросе убрал INT0 smile.gif,
но Вы успели отцировать первоначальный copy-paste вариант Ж(.

не суть важно, по условию задачи нам нужно не пропустить ни одного INT0. О всех остальных автор умолчал ;> Преполагаем что они не важны и их пропускать можно.
Цитата
Таких гарантий при нескольких источниках прерывания нет.
Заблуждение. И Вы сами это прекрасно понимаете, но не хотите сдаваться smile.gif
Гарантии достигаются за счет опять же двух составляющих:
- минимальный период;
- макс время обработки
каждого прерывания.

Если время обработки == 0, гарантия 100%.
Если период стремится к бесконечности - гарантия 100%.

ну а для всех остальных случаев гарантии оцениваются/считаются.
zltigo
Цитата(defunct @ Aug 28 2008, 23:26) *
не суть важно, по условию задачи....

Раз уж разговоры вышли за рамки задачи, то я намеренно ушел и от поминания конкретного прерывания.
Цитата
Гарантии достигаются за счет опять же двух составляющих:
- минимальный период;
- макс время обработки
каждого прерывания.

Повторяю последний раз. Ничего из вышеизложенного не гарантирует отсутствия вложенности вспомогательных прерываний. О повторном входе в обработчик и последствиях я речь вообще не вел и незачем подменять понятия. Однако, довести и до такого состояния систему которая прерывается каждые 64 такта на 63 такта ну очень легко sad.gif.
Цитата(defunct @ Aug 28 2008, 23:26) *
Если время обработки == 0, гарантия 100%.

Ну очень хорошая программа - научите писать такие smile.gif
Цитата
ну а для всех остальных случаев гарантии оцениваются/считаются.

Гарантия это НЕ ВЕРОЯТНОСТЬ. Гарантия либо она есть, либо ее НЕТ. Вот об отсутствии гарантий и веду речь.
defunct
Цитата(zltigo @ Aug 29 2008, 00:48) *
Повторяю последний раз. Ничего из вышеизложенного не гарантирует отсутствия вложенности вспомогательных прерываний.

гм?! с этим утверждением я не спорю и не спорил.

Цитата
О повторном входе в обработчик и последствиях я речь вообще не вел и незачем подменять понятия.

Почему же, как раз об этом и стоит говорить, т.к. только возможность повторного входа в тот же обработчик может привести к фатальным последствиям.
А вспомогательные прерывания - Бог с ними, они не мешают.

Цитата
Ну очень хорошая программа - научите писать такие smile.gif

в теории можно всё smile.gif

Цитата
Гарантия это НЕ ВЕРОЯТНОСТЬ. Гарантия либо она есть, либо ее НЕТ. Вот об отсутствии гарантий и веду речь.

Я о гарантии, не о вероятности.
Могу доказать на примере в системе с несколькими прерываниями, что есть гарантия того, что повторный вход в любой обработчик никогда не успеет произойти.
zltigo
Цитата(defunct @ Aug 28 2008, 23:59) *
гм?! с этим утверждением я не спорю и не спорил.

Ладно, допустим, что я забыл Ваши примеры с таймерами.
Тогда все остальное я просто не читаю, поскольку не знаю, с кем Вы спорите - похоже с собой sad.gif
=GM=
Цитата(PhX @ Aug 28 2008, 15:53) *
как сделать так, чтобы прерывание INT0 имело наивысший приоритет и прерывало обработчики остальных прерываний?

В остальных прерываниях ставите разрешение прерываний (sei) как можно ближе к точке входе, да и всё.

Однако, поскольку период появления INT0 может быть достаточно маленьким, всего 4 мкс, эффективность работы вашей сишной программы (в смысле загрузки процессора) находится под большим вопросом.

Пмсм вам надо пересмотреть принцип обработки прерываний от энкодера. Что если один выход энкодера подключить к счётчику0, а второй к счётчику2? Ёмкость счётчиков не позволит потерять ни одного импульса, а анализ показаний счётчиков даст возможность определить направление вращения.
zltigo
Цитата(=GM= @ Aug 29 2008, 01:11) *
..да и всё.

или нет smile.gif
Цитата
Пмсм вам надо пересмотреть принцип обработки прерываний от енкодера.

А вот это точно.
defunct
Цитата(zltigo @ Aug 29 2008, 01:37) *
Ладно, допустим, что я забыл Ваши примеры с таймерами.

А что не так с примерами-то? Они всего лишь показывают, что ресурса процессора хватит на обработку всех прерываний в real-time.

Цитата
Тогда все остальное я просто не читаю, поскольку не знаю, с кем Вы спорите - похоже с собой

Я не спорю, а утверждаю, что задачу автора ветки
Цитата
Есть три обработчика прерываний:
От Timer1
от INT0
и допустим
от Timer2
Прерывание от INT0 наиважнейшее не обработается вовремя расстрел (считает импульсы энкодера максимальная частота 250 КГц). Вопрос как сделать так, чтобы это прерывание имело наивысший приоритет и прерывало обработчики остальных прерываний?


можно решить двумя путями:
1) разрешать прерывания (sei) сразу на входе в обработчиках таймеров.
2) сократить латентность всех без исключения обработчиков до (Fclk / 250kHz) - (2 * Tcall + 1) тактов.
zltigo
Цитата(defunct @ Aug 29 2008, 02:19) *
Они всего лишь показывают...

Устал я об одном и том-же....
Цитата
...утверждаю, что задачу автора ветки можно решить двумя путями:

Не двумя путями, а ЕЩЕ двумя путями
Цитата
1) разрешать прерывания (sei) сразу на входе в обработчиках таймеров.
2) сократить латентность всех без исключения обработчиков до (Fclk / 250kHz) - (2 * Tcall + 1) тактов.

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

Все.
defunct
Цитата(zltigo @ Aug 29 2008, 11:18) *
-При этом первый путь тянет за собой многократную вложенность прерываний, пожирание стека,

Любой путь с разрешением прерываний в обработчиках тянет за собой все это, в т.ч. и предложенный Вами в #2.

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

Эта возможность пресекается. Это не ARM, здесь очень прогнозируемый КП.

Цитата
-А второй путь вообще сродни совету в ответ на жалобы на проблемы - ну а почему-бы Вам не быть богатым и здоровым?

посмотреть листинги обработчиков, которых всего три и посчитать время их выполнения. Вынести обработку в основной цикл программы. Что мешает обработчики того же таймера построить как-то так:

Код
ISR_T1()
{
    T1Event = TRUE;
    T1Counter += 1;
}

ISR_T2()
{
    T2Event = TRUE;
    T2Counter += 1;
}


main()
{
    ...

    for(;;)
    {
        sleep();

        if (T1Event)
        {
            T1Event = FALSE;
            Handle_T1Event(T1Counter);
        }
        if (T2Event)
        {
            T2Event = FALSE;
            Handle_T2Event(T2Counter);
        }
    }
}
=GM=
Прям операционка получилась...кооперативная(:-).
zhevak
Цитата(=GM= @ Aug 29 2008, 17:06) *
Прям операционка получилась...кооперативная(:-).


почитайте, разрядитесь smile.gif
http://ru.thedailywtf.com/Articles/Proklyataya-dverb.aspx
bzx
Цитата(PhX @ Aug 28 2008, 20:53) *
Прерывание от INT0 наиважнейшее не обработается вовремя расстрел (считает импульсы энкодера максимальная частота 250 КГц).

Если в задачи int0 входит только тупой подсчёт импульсов, то не проще ли, я бы да же сказал рациональнее, было бы завести данный сигнал не на int0, а на счётный вход таймера, который аппатарно будет считать импульсы, и не спеша далее делать свои дела. Да же на C все можно следать при таком подходе.
zhevak
Взгляд со стороны. Ибо в жаркой дискусси я неучвствовал, хотя все постинги прочитал.

Попробую объяснить на пальцах.

1. Возмите у жены/сестры/матери сантиметр (хотя, там все 150 сантиметров! Почему они его сантиметром называют -- отдельный вопрос.) -- это будет шкала времени. Пусть 10 сантиметров (длины) будут равены 1 мс.

2. Теперь прикиньте, сколько времени потребуется для обрабтки INT0. Допустим, 2 мс. Эквивалентная длина 20 сантимов. Нарежьте несколько полосок цветной бумаги (или провода) этой длины. Из условия задачи мы знаем, что INT0 прет с частотой 250 кГц. Значит, INT0 будет возникать через каждые 4 мс. Теперь, разложите цветные полоски на шкале времени через каждые 40 сантиметров. Это время занято.

3. Проделайте те же действия с плолосками бумаги другого цвета для проерываний от таймеров. Разложите "таймерные" полоски на свободном месте, с учетом примерной периодичности поступления этих прерываний.

4. Если есть еще какие-то процессы -- добавьте еще бумаги.


Понятно, что какие-то из полосок придется сдвигать вправо по оси времени. Сейчас главное уяснить два вопроса:

1) А хватит-ли вообще процессорного МК времени для обработки прерываний?
2) То, на сколько может сдвинуться полоска (обработка), это будет дрожжание обработчика -- ждиттер. Нужно прикинуть, оценить величину этого дрожжания.

Если после раскладки полосок остается еще достаточно времени (например более половины) и допустим джиттер, то задачу впринципе можно решить. Если плоски наезжают друг на друга, разумеется, -- нет.


Итак, дело за автором топика: внесите ясность.

ЗЫ
Для прерываний, которые не привязаны жестко ко времени (у которых полоски бумаги допустимо сдвигать) можно предложить следующий способ обработки. Обнаруженное, но необработанное событие (postpone) можно зафиксировать не в переменной-флаге, а в переменной-счетчике. Т.е. возникло прерывание, уходим в обработчик, инкрементируем счетчик, выходим. Все! Достаточно быстро, ничего лишнего, никакой обработки. Обработка будет потом.

В основном цикле программы, если какой-то счетчик больше нуля вызываем соответствующую функцию-обработчик события. В этой функции производим декремент счетчика.

Тут правда есть подводные камни. У AVR отсутствуют команды, котрые позволяют производить операцию инкремента/декремента в памяти. Значит, что бы декрементировать счетчик нужно сначала переменную считать в регистр, затем декрементировать, а после этого записать обратнно в память. Т.е. нарушается принцип атомарности операции. Что произойдет, если мы считали переменную в регистр, а в это время произошло прерывание, где эта же переменная успешно будет инкрементирована. Получается так, что после окончания прерывания, значение в регистре будет не верным. картина Репина "А мужики-то не знают!"

Для решения этого придется эту переменную либо размещать в регистре (расточительно и небезопасно), либо обрамлять тройку команд (ld-dec-st) командами разрешения/запрещения прерываний. Т.е. нужен нам нужен какой-то семафор. У АВР есть хорошая атомарная команда swap. Можно поиграться сней. Но я, кажись, увлекся... Извините smile.gif
Боинг749
Вот кстати ещё тема с похожей тематикой (касающаяся гимора с отстутствием к AVR как таковых приоритетов прерываний)
PhX
Честно говоря не ожидал, что проблема вызоет такую дискуссию.
На первый взгляд можно сделать следующий вывод: необходимо упростить задачу, поскольку чем больше сложность, тем дольше этап отладки. Поэтому прерывание от Timer2 необходимо убрать.
Господин zhevak предложил очень интнремный способ планирования задачи. Спасибо, но поскольку боюсь ножниц, воспользуюсь карандашом. smile.gif
Цитата(bzx @ Aug 29 2008, 22:52) *
завести данный сигнал не на int0, а на счётный вход таймера...

К большому сожалению считывание импульсов с энкодера несколько сложнее чем просто суммирование импульсов, об этом на форуме уже проводилась дискуссия. На более развитых нежели AVR контроллерах обработчик энкодера железно включен в перифирию, однако у AVR к сожалению этого нет. Приведу код обработчика INT0
Код
// Внешнее прерывание INT0
// На вход INT0 приходят импульсы от выхода A энкодера
// На вход PD0 приходят импульсы от выхода B энкодера
ISR(INT0_vect)
{
Rot = (PIND & 0x01 == 0x01);
if (Rot) pos++; else pos--;
}

Обработчик прерывания от Timer1 должно прерываться прерыванием от ISR0.
Как ли это делается в WinAVR?
Код
ISR(TIMER1_OVF_vect)
{
  sei(); // Разрешаем прерывания
  // Что-то сюда еще наверное нужно вставить чтобы впускать только прерывания от INT0
  if (PORTC < 0x08)
    PORTC<<=1;
  else
    PORTC = 0x01;
  TCNT1 = 0x10000 - 62500 / Frq;
}
Rst7
Дам пару советов. Во первых, код написать таким образом (правда, это для IAR, но для гнуся все остается в силе)
Код
#pragma vector=INT0_vect
__interrupt void INT0_proc(void)
{
  unsigned char t=pos-1;
  if (PIND&0x01) t+=2;
  pos=t;
}

#pragma vector=TIMER1_OVF_vect
__interrupt void T1_proc(void)
{
  unsigned char t;
  __enable_interrupt();
  t=PORTC;
  t<<=1;
  if (PORTC&0x08) t=1;
  PORTC=t;
  TCNT1 = 0x10000 - 62500 / Frq;
}


Общая идея в том, чтобы не работать с глобальными переменными, а работать с регистровыми. Например, обработчик INT0 после переделки занимает 27 тактов со всеми входами и выходами (это результат IAR'а).

Далее, если Вы боитесь, что в прерывании от переполнения Timer1 возможен второй вход - поставьте его в режим Clear On Compare Match и в OCR1 занесите 1. В результате, после переполнения, произойдет прерывание, а таймер дальше не пойдет, его все время Compare Match будет сбрасывать. Как только Вы занесете в таймер значение больше 1 - опять начнется отсчет.
Сергей Борщ
Цитата(PhX @ Aug 30 2008, 09:26) *
Как ли это делается в WinAVR?

Позволю себе поправить:
Код
ISR(TIMER1_OVF_vect)
{
  uint8_t TIMSK_save = TIMSK;
  TIMSK = 0;
  // Что-то сюда еще наверное нужно вставить чтобы аналогично запретить остальные прерывания кроме INT0

  sei(); // Разрешаем прерывания
  // первым делом обновляем счетчик таймера. Если мы сделаем это позже,
  // то возможна ситуация, когда при обновлении счетчик "перепрыгнет" нулевое значение и прерывание будет потеряно.
  // с момента возникновения прерывания таймер мог уже что-то насчитать, поэтому для
  // увеличения точности не присвоение, а сложение
  TCNT1 += 0x10000 - 62500 / Frq;
  if (!(PORTC & (1 << 3)))   // это компилится в одну инструкцию sbis вместо in, and, jne
    PORTC<<=1;
  else
    PORTC = 0x01;
  cli();
  TIMSK = TIMSK_save;
}
Rst7
Цитата
 if (!(PORTC & (1 << 3)))   // это компилится в одну инструкцию sbis вместо in, and, jne    PORTC<<=1;  else    PORTC = 0x01;


Вот не согласен. Есть же else smile.gif Так что лучше написать так, как у меня (у меня можно проверить и регистр, но IAR почему-то не хочет компилить SBRS, хотя SBIS изготавливает влет).

Цитата
 TCNT1 += 0x10000 - 62500 / Frq;


Это надо бы делать до SEI. А вообще данное место зависит от необходимой точности частоты прерываний от таймера. Точно будет только при использовании Clear On Compare Match и прерывания по Compare.
Сергей Борщ
Цитата(Rst7 @ Aug 30 2008, 12:27) *
Это надо бы делать до SEI.
Нет. Эта операция довольно долгая (16-битный таймер как-никак), до sei() она будет увеличивать латентность. А в этом месте прерывания от таймеров остались запрещены, поэтому делать ее в этом месте вполне безопасно. Если можно вообще говорить о безопасности при доступе к 16-битному работающему таймеру на 8-битной машине. Насчет Clear On Compare Match абсолютно согласен.
sKWO
Цитата(Сергей Борщ @ Aug 30 2008, 12:38) *
Нет. Эта операция довольно долгая (16-битный таймер как-никак), до sei() она будет увеличивать латентность. А в этом месте прерывания от таймеров остались запрещены, поэтому делать ее в этом месте вполне безопасно. Если можно вообще говорить о безопасности при доступе к 16-битному работающему таймеру на 8-битной машине. Насчет Clear On Compare Match абсолютно согласен.

Опасения конечно есть, но при чтении с 16-ти битных регистров у АВР предусмотрен временный регистр. При чтении сначала младший байт помещается в тмп, после этого производится чтение. ИАР это делает правильно.
Rst7
Цитата
оэтому делать ее в этом месте вполне безопасно.


Не, я про то, что если возникнет прерывание между чтением TCNT1 и установкой - будет уж очень большая погрешность.

Тогда уж лучше так:
Код
__interrupt void T1_proc(void)
{
  unsigned char t;
  __enable_interrupt();
  t=PORTC;
  __disable_interrupt();
  TCNT1+=_ADJUST_T1_+0x10000 - 62500 / Frq;
  __enable_interrupt();
  t<<=1;
  if (PORTC&0x08) t=1;
  PORTC=t;
  __disable_interrupt();
  TCNT1 = 0x10000 - 62500 / Frq;
}


_ADJUST_T1_ - это поправочная константа на время между чтением TCNT1 и записью. Ее надо выбрать по результирующему коду обработчика.

А чтобы не бояться повторного входа, установить заранее OCR1 на число меньшее, чем заносится в TCNT1 (или, например, неплохо будет занести туда время, которое допустимо между вызовами прерывания). По флагу OCF1 можно ориентироваться, что произошло гуано в генерации частоты.

На самом деле это хорошо работает, если прескаллер равен 1.

Кстати, я надеюсь у автора Frq - константа? Если нет, то расчет значения для занесения в таймер надо убирать из прерывания.
Боинг749
Цитата(PhX @ Aug 28 2008, 20:53) *
Есть три обработчика прерываний:
От Timer1
от INT0
и допустим
от Timer2
Прерывание от INT0 наиважнейшее не обработается вовремя расстрел (считает импульсы энкодера максимальная частота 250 КГц). Вопрос как сделать так, чтобы это прерывание имело наивысший приоритет и прерывало обработчики остальных прерываний?
Компилятор WinAVR.

Дык в ATmega-х прерывание INT0 и так "наиважнейшее" поэтому его вектор стоит на самом 1-м месте. Просто при входе в обработчики таймерных прерываний делайте глобальное разрешение прерываний командой SEI и проектируйте систему так, чтобы не было вложенных обработчиков( ну когда обработчик не успел ещё одно прерывание отработать, а уже наступило следующее).. А если вложенные обработчики Вас не пугают и Вы анализируете флаг повторной входимости, тогда вообще Ноу Проблем
defunct
Цитата
TCNT1 += 0x10000 - 62500 / Frq;

выбросить все манипуляции с TCNT.
Работать нужно только с OCR, и пользовать CTC режим.
Боинг749
Цитата(defunct @ Aug 31 2008, 00:59) *
выбросить все манипуляции с TCNT.
Работать нужно только с OCR, и пользовать CTC режим.

А причём тут это? Какое отношение Ваши реплики имеют к вопросу темы? НАПОМИНАЮ, вопрос темы звучал так:
Цитата(PhX @ Aug 28 2008, 20:53) *
как сделать так, чтобы это прерывание <INT0>{прим.Боинг749} имело наивысший приоритет и прерывало обработчики остальных прерываний?

И КОНКРЕТНО на этот вопрос я ответил чуть выше
SasaVitebsk
В одном из изделий у меня использовался следующий протокол.
Шли импульсы частотой 32кГц. Длительность импульсов: 0 - 2мкс, 1 - 4мкс.
Это обрабатывалось по INT. Кроме этого работал USART на частоте 115200 и таймерное прерывание.
Потеря 1 бита была вполне определима.
На частоте 7372800 atmega8 прекрасно справляется с данной задачей. Никаких проблем нет. Используются вложенные прерывания.
PhX
Цитата(defunct @ Aug 31 2008, 01:59) *
выбросить все манипуляции с TCNT.
Работать нужно только с OCR, и пользовать CTC режим.

Абсолютно согласен. Уже попробывал моргание светодиодом. smile.gif Просто с AVR знаком 3-й день не знаю всех особенностей.
Текущий проект удалось упростить до использования 2-х прерываний, так что проблем быть не должно.
А вообще, с теоретической точки зрения, интересный вопрос, для более сложных проектов весьма актуальный.
defunct
Цитата(Боинг749 @ Aug 31 2008, 00:08) *
Какое отношение Ваши реплики имеют к вопросу темы?

Прямое - минимизация латентности обработчиков прерываний.
Почитайте весь топик.
Боинг749
Цитата(defunct @ Aug 31 2008, 11:34) *
Прямое - минимизация латентности обработчиков прерываний.
Почитайте весь топик.

Дык я же ответил: просто надо сразу при входе в обработчик таймера разрешать прерывания командой SEI. Причём тут режимы работы таймера?
Сергей Борщ
Цитата(Боинг749 @ Aug 31 2008, 00:08) *
НАПОМИНАЮ, вопрос темы звучал так:
Напомните, с какого момента вы были наделены правами и обязанностями модератора?
Rst7
Цитата
с какого момента вы были наделены правами и обязанностями модератора?


Свят-свят... Только дохтура нам модератором не хватало...

Кстати, а что правила говорят о повторной регистрации? Явно ж клиент на бан.

Пардон за офтоп.
=GM=
Цитата(Rst7 @ Aug 30 2008, 07:31) *
Общая идея в том, чтобы не работать с глобальными переменными, а работать с регистровыми. Например, обработчик INT0 после переделки занимает 27 тактов со всеми входами и выходами (это результат IAR'а)

Интересно бы взглянуть на код. Хотя по смыслу переменные t, pos должны быть 16-битными, если не 32-битными. У меня для целых (16-битных) на асме получилось 18 тактов.

А вообще вывод неутешительный, время выполнения двух прерываний порядка 83 тактов, явно подпадает под расстрельную статью...
defunct
Цитата(=GM= @ Sep 1 2008, 01:01) *
А вообще вывод неутешительный, время выполнения двух прерываний порядка 83 тактов, явно подпадает под расстрельную статью...

Таймер тоже должен шлепать прерывания c частотой 250kHz?
Если нет, то все Ок. Иначе сокращать дальше - писать обработчики на asm'e.
Rst7
Цитата
Интересно бы взглянуть на код


Код
        RSEG CODE:CODE:NOROOT(1)
//  147 __interrupt void INT0_proc(void)
INT0_proc:
//  148 {
        ST      -Y, R17
        ST      -Y, R16
        IN      R17, 0x3F
//  149   unsigned char t=pos-1;
        LDS     R16, pos
        DEC     R16
//  150   if (PIND&0x01) t+=2;
        SBIC    0x09, 0x00
        SUBI    R16, 254
//  151   pos=t;
??INT0_proc_0:
        STS     pos, R16
//  152 }
        OUT     0x3F, R17
        LD      R16, Y+
        LD      R17, Y+
        RETI
        REQUIRE _A_PIND
//  153
//  154 #pragma vector=TIMER1_OVF_vect

        RSEG CODE:CODE:NOROOT(1)
//  155 __interrupt void T1_proc(void)
T1_proc:
//  156 {
        ST      -Y, R18
        ST      -Y, R17
        ST      -Y, R16
        IN      R18, 0x3F
//  157   unsigned char t;
//  158   __enable_interrupt();
        SEI
//  159   t=PORTC;
        IN      R16, 0x08
//  160   t<<=1;
        LSL     R16
//  161   if (PORTC&0x08) t=1;
        SBIC    0x08, 0x03
        LDI     R16, 1
//  162   PORTC=t;
??T1_proc_0:
        OUT     0x08, R16
//  163   TCNT1 = 0x10000 - 62500 / Frq;
        LDI     R16, 250
        LDI     R17, 255
        STS     133, R17
        STS     132, R16
//  164 }
        OUT     0x3F, R18
        LD      R16, Y+
        LD      R17, Y+
        LD      R18, Y+
        RETI
        REQUIRE _A_TCNT1
        REQUIRE _A_PORTC


А старший байт pos (если надо) - лучше править не в процедуре прерывания, а отдельно.
Dog Pawlowa
Цитата(defunct @ Sep 1 2008, 02:23) *
Таймер тоже должен шлепать прерывания c частотой 250kHz?
Если нет, то все Ок. Иначе сокращать дальше - писать обработчики на asm'e.

Ну не знаю, кто как, но я зарекся делать системы без запаса 50%.
А если есть вложенные прерывания, риск многократно возрастает. Недавно влетел с прерыванием "самодельного" IrDA - прибор эксплуатировался в помещении с инфракрасным фоном и последствия были удручающие :-(
PhX
Цитата(Dog Pawlowa @ Sep 1 2008, 12:36) *
Ну не знаю, кто как, но я зарекся делать системы без запаса 50%.

Само собой, 250 кГц, для текущего проека, это запас примерно 1000%. smile.gif
250 кГц были определены среднепотолочным методом, просто с учетом планов на будущее.
Реально прерывания от таймера "шлепают" с частотой 12 кГц, а от INT0 с частотой около 30 кГц, причем это тоже, вероятно с запасом. smile.gif

p.s. А в WinAVR можно результат работы компилятора как в посте Rst7, да и где взять время выполнения команд? Вообщем как можно быстро расчитать время обработки прерывания?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.