2008-10-17 3 views
2

Я использую микроконтроллер с ядром C51. У меня есть довольно временная и большая подпрограмма, которую нужно называть каждые 500 мс. RTOS не используется.Точно время вызова функции

То, как я делаю это прямо сейчас, заключается в том, что у меня есть существующее прерывание по таймеру 10 мс. Я устанавливаю флаг после каждых 50 прерываний, которые проверены на то, что они истинны в основном программном цикле. Если флаг является истинным, вызывается подпрограмма. Проблема в том, что к тому моменту, когда цикл программы подходит для обслуживания флага, он уже превышает 500 мс, иногда даже> 515 мс в случае определенных путей кода. Принятое время неточно предсказуемо.

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

Есть ли способ гарантировать, что подпрограмма вызывается ровно 500 мсек каждый раз?

ответ

0

Будет ли это делать то, в чем вы нуждаетесь?

#define FUDGE_MARGIN 2 //In 10ms increments 

volatile unsigned int ticks = 0; 

void timer_10ms_interrupt(void) { ticks++; } 

void mainloop(void) 
{ 
    unsigned int next_time = ticks+50; 

    while(1) 
    { 
     do_mainloopy_stuff(); 

     if(ticks >= next_time-FUDGE_MARGIN) 
     { 
      while(ticks < next_time); 
      do_500ms_thingy(); 
      next_time += 50; 
     } 
    } 
} 

NB: Если вы получили за обслуживание с вашей задачей каждый-500мс, то это будет очередь их, что не может быть то, что вы хотите.

+0

У этого подхода есть проблема с упаковкой: если вы выходите из if-statement с тиками == 65500 и next_time == 14 (65550-65536) Я выполняю ваш код 500ms каждый раз через цикл, пока галочки не обернутся вокруг. Также вы должны увеличить next_time ПЕРЕД вызовом do_500ms(). – paxdiablo 2008-10-21 03:49:35

+0

И поскольку вы добавляете 50 к next_time каждый раз, когда вы делаете_500 мс, он может быть в сотни или тысячи к тому времени, когда галочки обертывают, вокруг которых будет огромная задержка до вызова do_500ms() в следующий раз. – paxdiablo 2008-10-21 03:51:49

+0

Я использовал это уже со следующей модификацией, может быть, Майк Ф. может включить это: я сбросил тики на 0 непосредственно перед обслуживанием подпрограммы 500 мс. и исправлено next_time до 50. – 2008-10-23 09:19:33

1

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

Если эта работа должна произойти ровно в 500 мс, а затем запустить ее из рутины таймера и разобраться с этим. Это действительно то, что будет делать упреждающая RTOS.

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

+0

Я не могу назвать его из ISR, так как таймер не реентерабелен, и мне нужно вернуться из таймера ISR до следующего прерывания таймера. – 2008-10-17 10:34:28

+0

Вы не заслужили этого проголосовавшего, который я видел ... – Toybuilder 2008-10-21 23:29:34

1

Я не думаю, что есть способ гарантировать это, но это решение может стать приемлемой альтернативой.

Могу ли я предложить не, установив флаг, а вместо этого изменив значение?

Вот как это могло бы работать.

1/Запустить значение в ноль.

2/каждые 10 мс прерывать, увеличить это значение на 10 в ISR (процедура обслуживания прерываний).

3/В основном цикле, если значение> = 500, вычитайте 500 из значения и выполните свои 500 мс действия.

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

Это имеет то преимущество, что функция работает как можно ближе к границам 500 мс независимо от латентности или продолжительности.

Если по какой-то причине ваша функция запускается на 20 мс на одной итерации, значение уже будет 520, поэтому ваша функция будет установлена ​​на 20, что означает, что она будет ждать 480 мс до следующей итерации.

Мне кажется, что это лучший способ достичь того, чего вы хотите.

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

volatile int xtime = 0; 
void isr_10ms(void) { 
    xtime += 10; 
} 
void loop(void) { 
    while (1) { 
     /* Do all your regular main stuff here. */ 
     if (xtime >= 500) { 
      xtime -= 500; 
      /* Do your 500ms activity here */ 
     } 
    } 
} 
1

Хороший вариант - использовать RTOS или написать свой собственный простой RTOS.

ОСРВ для удовлетворения ваших потребностей нужно будет только сделать следующее:

  • график периодических задач
  • график круглых задач малиновки
  • контекст
  • преформ переключения

Ваши требования являются следующие:

  • выполнять периодическую задачу каждый 500ms
  • в дополнительное время между выполнять круглые задачи Робина (выполнение невремените критические операции)

ОСРВА, как это будет гарантировать 99,9% вероятность, что ваш код будет выполняться вовремя , Я не могу сказать 100%, потому что любые действия, которые вы выполняете в своих ISR, могут помешать работе RTOS. Это проблема с 8-битными микроконтроллерами, которые могут выполнять только одну команду за раз.

Написание RTOS сложно, но умело. Ниже приведен пример небольшой (900 строк) RTOS, ориентированной на 8-битную AVR-платформу ATMEL.

Report and Code создано для класса CSC 460: Операционные системы реального времени (в Университете Виктории).

0

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

0

Почему у вас есть критически важная процедура, которая так долго запускается?

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

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

Можете ли вы лучше описать, что делает эта длительная процедура, и для чего нужен определенный интервал?


Добавление на основе комментариев:

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

Чтобы взять ваш пример, если прерывание по таймеру установлено на периоды 10 мс, и вы знаете, что ваша сервисная процедура займет 89 мс, просто переходите и подсчитывайте 41 прерывание таймера, затем выполняйте свою активность 89 мс и пропустите восемь таймерных прерываний (42-й до 49).

Затем, когда ваш ISR выходит (и очищает ожидающее прерывание), «первое» прерывание следующего раунда в 500 мс произойдет примерно через ms.

Учитывая, что вы являетесь «ресурсом maxed», предполагается, что у вас есть другой таймер и источники прерываний, которые используются также, что означает, что полагаться на основной цикл, который должен быть точно установлен, не будет работать, потому что другие источники прерываний могут срабатывать в неподходящий момент.

0

Ах, еще одна альтернатива для рассмотрения - архитектура x51 допускает два уровня приоритетов прерывания. Если у вас есть определенная аппаратная гибкость, вы можете заставить один из внешних контактов прерывания быть поднятым таймером ISR с интервалом 500 мс, а затем разрешить обработку прерываний нижнего уровня вашего кода каждые 500 мс.

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

См часть 11.2 в этом документе я нашел в Интернете: http://www.esacademy.com/automation/docs/c51primer/c11.htm

0

Если я правильно interpretting ваш вопрос, у вас есть:

  • основной цикл
  • некоторые операции с высоким приоритетом, что должен выполняться каждые 500 мс, на срок до 89 мс
  • 10 мс таймер, который также выполняет небольшое количество операций.

Есть три варианта, как я его вижу. Первый - использовать второй таймер с более низким приоритетом для ваших 500 мсек операций. Вы все равно можете обработать прерывание на 10 мс и после полного продолжения обслуживания прерывания по таймеру 500 мс.

Второй вариант - вам действительно нужно обслуживать 10 мс прерывания каждые 10 мс? Он делает что-то другое, кроме времени? Если нет, и если ваше оборудование позволит вам определить количество тиков 10 мс, прошедших при обработке 500-секундного op (т. Е. Не используя сами прерывания), тогда вы можете запустить свой 500 мс в течение 10 мс прерывания и обработать 10 мс, которые вы пропустили, когда закончите.

Третий вариант: Чтобы следовать ответам Джастина Таннера, кажется, что вы можете создать собственное упреждающее многозадачное ядро, чтобы удовлетворить ваши требования без особых проблем. Кажется, все, что вам нужно, это две задачи: одна для главного супер-цикла и одна для вашей задачи 500 мс.

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

(я предполагаю, что, строго это гибрид упреждающей и кооперативной многозадачности, но это не важно сейчас)


редактировать: Существует простой четвертый вариант. Либерально перецьте свой главный суперцикл с проверкой того, прошло ли 500 мс, как до, так и после каких-либо длительных операций. Не точно 500 мс, но вы можете уменьшить задержку до допустимого уровня.

1

Вы также можете использовать два флага - это «Предварительное действие» флаг, и «триггер» флаг (с использованием Mike F в качестве отправной точки):

#define PREACTION_HOLD_TICKS (2) 
#define TOTAL_WAIT_TICKS (10) 

volatile unsigned char pre_action_flag; 
volatile unsigned char trigger_flag; 

static isr_ticks; 
interrupt void timer0_isr (void) { 
    isr_ticks--; 
    if (!isr_ticks) { 
     isr_ticks=TOTAL_WAIT_TICKS; 
     trigger_flag=1; 
    } else { 
     if (isr_ticks==PREACTION_HOLD_TICKS) 
      preaction_flag=1; 
    } 
} 

// ... 

int main(...) { 


isr_ticks = TOTAL_WAIT_TICKS; 
preaction_flag = 0; 
tigger_flag = 0; 
// ... 

    while (1) { 
     if (preaction_flag) { 
      preaction_flag=0; 
      while(!trigger_flag) 
      ; 
      trigger_flag=0; 
      service_routine(); 
     } else { 
      main_processing_routines(); 
     } 
    } 
} 
Смежные вопросы