Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Таймер и прерывания
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > ARM, 32bit
whiteTigr
Среда разработки: Keil.
Микроконтроллер: lpc2367.

Есть таймер, генерирующий прерывания. Таймер работает по принципу "Отсчитал до нужного MR, прервался и остановился".
Процедура, на которую он прыгает при прерывании переопределяемая.

Код
void InitTimer()
{
  /* Initialize Timer 2 */  
  PCONP |= PCTIM2;

  PCLKSEL1 &= ~(3 << PCLK_TIMER2_OFFSET);
  PCLKSEL1 |=  (1 << PCLK_TIMER2_OFFSET);

  T2PR = 63; // 1 mks  
  T2MR0 = 99; // 100 mks
  T2MCR = 7; // interupt, reset, stop  

  VICIntEnClr = INTTIM2;
  VICVectAddr26 = (unsigned long)Timer2Handler;
  VICVectPriority26 = 10;

  T2TCR = 0x01;

  VICIntEnable += INTTIM2;
}


Код
__irq void Timer2Handler()
{
  if (OnTimerInterupt)
    OnTimerInterupt();  
  
  T2IR = 0x01;
  VICVectAddr = 0;  
}


Код
typedef void (*CallBackFunction)(void);
CallBackFunction OnTimerInterupt = 0;


Допустим, я назначаю на прерывание функцию, которая выполняется около 1мс (пусть будет Func1).
При прерывании в этой функции запускаю этот же таймер, переопределяя OnTimerInterupt на функцию с небольшим временем выполнения (пусть будет Func2), и настраиваю таймер на срабатывание через 100 мкс.

Вопрос: как поведет себя выполняющаяся функция Func1, при следующем прерывании таймера (в котором вызовется Func2)?
whiteTigr
Сегодня дошло, что в первом сообщении глупость. В таком порядке прерывания явно не должны вызываться.

Пните пожалуйста в направлении хорошего объяснения механизма работы прерываний (возможно, вложеных). На форуме перечитал много, но общее впечатление от тем осталось в стиле "Я попрыгал с бубном и у меня получилось".
Из даташита на процессор мало что понятно, слишком скупая информация.

UPD: Ну или можно пнуть в направлении книжки по проектированию систем на микропроцессорах.
sergeeff
Цитата(whiteTigr @ Aug 16 2011, 10:28) *
Сегодня дошло, что в первом сообщении глупость. В таком порядке прерывания явно не должны вызываться.

Пните пожалуйста в направлении хорошего объяснения механизма работы прерываний (возможно, вложеных). На форуме перечитал много, но общее впечатление от тем осталось в стиле "Я попрыгал с бубном и у меня получилось".
Из даташита на процессор мало что понятно, слишком скупая информация.

UPD: Ну или можно пнуть в направлении книжки по проектированию систем на микропроцессорах.



А что конкретно непонятно?
whiteTigr
Цитата(sergeeff @ Aug 16 2011, 11:46) *
А что конкретно непонятно?


IRQ0 _______/-------
IRQ1 __/--------------

Первым пришло низкоприоритетное прерывание, уже во время этого прерывания приходит высокоприоритетное. Что происходит в этом случае?
а) Прерывается выполнение IRQ1 и начинает выполняться IRQ0?
б) Или сначала завершается обработка IRQ1 и только потом начинает выполняться IRQ0?

Ну и хотелось бы увидеть общие рекомендации по проектированию систем прерываний. Что можно делать, чего категорически нельзя, как желательно? Понимаю, что рано или поздно дойду до этого, но не хотелось бы топтать поле с граблями.
sergeeff
Вы же ARM'ами заняты? Там всего два прерывания IRQ и FIQ. Зато есть VIC (vectored interrupt controller). Он обеспечивает обработку до 16 irq и одного fiq. Сам VIC обеспечинает возможность приоритетной обработки прерываний. Дело в том, как вы напишите собственно обработчик. Он может обеспечивать вложенные прерывания, а может нет. При любом варианте если приходит несколько прерываний, выполняется первым более высокоприоритетное.

Посмотрите, например, http://winarm.scienceprog.com/arm-mcu-type...ontrollers.html
whiteTigr
Цитата(sergeeff @ Aug 16 2011, 12:50) *
Вы же ARM'ами заняты?

Ухук.

Цитата(sergeeff @ Aug 16 2011, 12:50) *
Там всего два прерывания IRQ и FIQ. Зато есть VIC (vectored interrupt controller). Он обеспечивает обработку до 16 irq и одного fiq.

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

Цитата(sergeeff @ Aug 16 2011, 12:50) *
Цитата(whiteTigr @ Aug 16 2011, 11:52) *

IRQ0 _______/-------
IRQ1 __/--------------

Первым пришло низкоприоритетное прерывание, уже во время этого прерывания приходит высокоприоритетное. Что происходит в этом случае?
а) Прерывается выполнение IRQ1 и начинает выполняться IRQ0?
б) Или сначала завершается обработка IRQ1 и только потом начинает выполняться IRQ0?

Сам VIC обеспечинает возможность приоритетной обработки прерываний. Дело в том, как вы напишите собственно обработчик. Он может обеспечивать вложенные прерывания, а может нет. При любом варианте если приходит несколько прерываний, выполняется первым более высокоприоритетное.

Это если одновременно приходят? Одновременный приход видится идеальным случаем.
В моей платке получалось что главный таймер с приоритетом 12 умудрялся тормозить прерывания с приоритетами 7 и 10. Насколько я понял по временам - задержка как раз равна задержке на обработку в главном таймере. Т.е. все происходило по варианту (б).

Цитата(sergeeff @ Aug 16 2011, 12:50) *
Посмотрите, например, http://winarm.scienceprog.com/arm-mcu-type...ontrollers.html

Цитата
Non-vectored interrupts have the lowest priority.
Vectored IRQ have middle priority.

Возможно что-то с этим связано.
Vectored - это объявленные через VICVectAddr[N].
Non-vectored - через VICDefVectAddr.
Так?
sergeeff
Как-то я с VIC'ом не работал, только с AIC'ом. При невложенных прерываниях, пока не отработает текущий обработчик, следующий не вызывается. Посему все обработчики пишем как можно короче и шустрее.
whiteTigr
Цитата(sergeeff @ Aug 16 2011, 14:00) *
Как-то я с VIC'ом не работал, только с AIC'ом. При невложенных прерываниях, пока не отработает текущий обработчик, следующий не вызывается. Посему все обработчики пишем как можно короче и шустрее.

И в главном цикле while(1) анализируем выставленные флаги в прерываниях...
Мне вот сказано, что я должен ответить по RS485 через 200мкс после окончания посылки ко мне. Т.е. делается прерывание на прием байта, по последнему байту пакета запускается таймер на 200мкс, по прерыванию от него посылается ответ.
Прерывание на прием набивает байты в пакет сразу же (нужно же определять конец пакета) - уже не сильно короткая получается, но и вынести из прерывания, по-моему, не получится.
Прерывание по таймеру должно затолкать подготовленный для отправки пакет в выходной буфер.
И получается что прерывание главного таймера (громко сказано "главного", приоритет у него наименьший) на обработку кнопок и дисплея мешает нормальной работе системы. Вот при сокращении главного таймера до Counter++ (и анализа Counter'a в while(1)) все заработало нормально.

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

P.S.: чувствую, что задаю настолько глупые вопросы, что стыдно становится. sad.gif
sergeeff
Цитата(whiteTigr @ Aug 16 2011, 13:26) *
И в главном цикле while(1) анализируем выставленные флаги в прерываниях...
Мне вот сказано, что я должен ответить по RS485 через 200мкс после окончания посылки ко мне. Т.е. делается прерывание на прием байта, по последнему байту пакета запускается таймер на 200мкс, по прерыванию от него посылается ответ.


Ну и на кой выжидать 200 мкс после последнего байта, если у вас и так полно всякой работы? Кстати, у вас процессор на нормальной частоте работает? Какие-нибудь benchmark тесты не пробовали?

А про вложенные прерывания. Возьмите и попробуйте, примеров достаточно, как правильно организовать. На сайте, правда, полно критиков вложенных прерываний, но у вас же есть своя голова на плечах.
whiteTigr
Цитата(sergeeff @ Aug 16 2011, 15:40) *
Ну и на кой выжидать 200 мкс после последнего байта, если у вас и так полно всякой работы? Кстати, у вас процессор на нормальной частоте работает? Какие-нибудь benchmark тесты не пробовали?

200мкс по ТЗ указано. Идея была в том, что я запускаю таймер, по прерыванию которого отправлю подготовленную посылку. Ожидание, естественно не через "for (i = 0; i < ...; i++);" сделано.
Резонатор стоит на 16МГц. Судя по настройкам uart'a эта частота умножается на 4 где-то внутри.
Benchmark'и не пробовал (только сейчас узнал о них).

Цитата(sergeeff @ Aug 16 2011, 15:40) *
А про вложенные прерывания. Возьмите и попробуйте, примеров достаточно, как правильно организовать. На сайте, правда, полно критиков вложенных прерываний, но у вас же есть своя голова на плечах.

Хорошо, попробую поискать.
По этому форуму я уже предостаточно предостережений нашел. Чувствую, что не с пустого места такое возникает, и моё желание использовать вложеность - это мысли новичка, которые я должен искоренить.
toweroff
Цитата(whiteTigr @ Aug 16 2011, 15:49) *
200мкс по ТЗ указано. Идея была в том, что я запускаю таймер, по прерыванию которого отправлю подготовленную посылку.

ойой
может, флаг выставляете? по которому в основном теле что-то отправляется?
whiteTigr
Цитата(toweroff @ Aug 16 2011, 20:22) *
ойой
может, флаг выставляете? по которому в основном теле что-то отправляется?


Код
void StartResponseTimer()
{
  StopTimer();
  T2TC = 0;
  T2MR0 = 199; // 200 mks
  OnTimerInterupt = &OnResponse;
  StartTimer();
}

void OnResponse()
{
  int i;
  for (i = 0; i < OutBufP; i++)
  {
    RS485SendByte(OutBuf[i]);
  }
}

...

void RS485SendByte(char byte)
{
  TBuffer *buffer = &RS485.transmiteBuffer;  

  (*buffer).data[(*buffer).addrWrite] = byte;
  (*buffer).addrWrite = ((*buffer).addrWrite + 1) % BufferSize;
  (*buffer).count++;

  // Если ничего не отправляется, то инициируем отправку
  // Иначе байт отправится после отправки предыдущего
  if (DE_PIN() == 0)
  {
    RS485SendByte_lowlevel();
  }
}


Сильно криво?
В SendByte никаких ожиданий, просто заталкивание посылки в выходной буфер.

UPD:
Код
  (*buffer).addrWrite = ((*buffer).addrWrite + 1) % BufferSize;

После поста на форум эта строчка начала "резать глаз".
Переписал в виде:
Код
  buffer->addrWrite++;
  if (buffer->addrWrite >= BufferSize)
    buffer->addrWrite = 0;
sergeeff
Тогда уж напишите:

Код
if (++buffer->addrWrite == BufferSize)
    buffer->addrWrite = 0;


Код
buffer->addrWrite > BufferSize
никогда не бывает
whiteTigr
Цитата(sergeeff @ Aug 17 2011, 10:28) *
Тогда уж напишите:

Код
if (++buffer->addrWrite == BufferSize)
    buffer->addrWrite = 0;


Код
buffer->addrWrite > BufferSize
никогда не бывает


Недолюбливаю сравнивать через "==". Хотя тут можно и так.
Несколько раз уже натыкался на то, что граница вначале константа, потом, в определенный момент, превращается в переменную и все сравнения через "==" приводят к зависанию программы.

P.S.: посмотрел на генерируемый код, вернул первый вариант. При размере BufferSize равного степени двойки он генерирует
add r0, r0, #1
and r0, r0, <mask>
toweroff
если размер буфера равен степени двойки, то вот так не нагляднее?
Код
(*buffer).data[(*buffer).addrWrite++ & (BUFLEN-1)] = byte;
whiteTigr
Цитата(toweroff @ Aug 17 2011, 12:40) *
если размер буфера равен степени двойки, то вот так не нагляднее?
Код
(*buffer).data[(*buffer).addrWrite++ & (BUFLEN-1)] = byte;


Может быть. Правда, в таком случае, любое использование addrWrite будет с маской.
toweroff
Цитата(whiteTigr @ Aug 17 2011, 14:06) *
Может быть. Правда, в таком случае, любое использование addrWrite будет с маской.

да оно, фактически, в двух местах и будет использоваться - при инициализации и при отправке sm.gif
whiteTigr
Цитата(toweroff @ Aug 17 2011, 14:08) *
да оно, фактически, в двух местах и будет использоваться - при инициализации и при отправке sm.gif

Чувствуется, речь о фломастерах зашла. sm.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.