реклама на сайте
подробности

 
 
4 страниц V  < 1 2 3 4 >  
Reply to this topicStart new topic
> Как считать значение аппаратно-программного таймера...
Alechin
сообщение Nov 7 2006, 12:34
Сообщение #16


Частый гость
**

Группа: Свой
Сообщений: 158
Регистрация: 27-06-05
Из: Химки, Моск.обл.
Пользователь №: 6 334



Цитата
Че й то я так и не понял проблемы. Я делаю вот так:
[code]
/* системное время */
ULONG g_SysTIME_ms; /* милисекунды */

/*****************************************************************************
Обработчик прерывания ТС1 по сравнению TCCR1A
******************************************************************************/
#pragma vector = TIMER1_COMPA_vect
__interrupt void TIMER1_COMPA_isr(void)
{
g_SysTIME_ms++;
}

Отличие в том, что Ваш счетчик системного времени полностью программный (т.е. по каждому системному тику Вы программно инкрементируете счетчик системного времени). Это возможно только при низкой разрешающей способности - т.е. при микросекундной точности Вам пришлось бы прерываться для инкремента каждую микросекунду. У меня же младшее слово счетчика системного времени инкрементируется аппаратно (это счетный регистр таймера). Соотв. накладные расходы на такой таймер - одно прерывание по переполнению таймера за 65536 мксек. Вот тут то и проблема - пол счетчика аппаратные, половина нет.
Go to the top of the page
 
+Quote Post
prottoss
сообщение Nov 7 2006, 12:54
Сообщение #17


Гуру
******

Группа: Свой
Сообщений: 2 720
Регистрация: 24-03-05
Пользователь №: 3 659



Цитата(Alechin @ Nov 7 2006, 19:34) *
Отличие в том, что Ваш счетчик системного времени полностью программный (т.е. по каждому системному тику Вы программно инкрементируете счетчик системного времени). Это возможно только при низкой разрешающей способности - т.е. при микросекундной точности Вам пришлось бы прерываться для инкремента каждую микросекунду. У меня же младшее слово счетчика системного времени инкрементируется аппаратно (это счетный регистр таймера). Соотв. накладные расходы на такой таймер - одно прерывание по переполнению таймера за 65536 мксек. Вот тут то и проблема - пол счетчика аппаратные, половина нет.
Вы внимательно на код смотрите. Точнее вот на эту строку

Код
/* запоминаем текущее время */
    Pulse.Time[Pulse.Start] = MAKEULONG(LOWORD(g_SysTIME_ms),TCNT1);


Да, я выложил прошлый раз не весь код, вот функция которая возвращает время между двумя пришедшими импульсами:
Код

/*****************************************************************************
Возвращает значение промежутка времени между двумя импульсами в мкс
Так как значение времени не есть чистое шестнадцатиричное число (потому что
старшее слово - есть количество 2-х тысячных значений младшего слова), нам
приходится преобразовывать значения времени к нормальному шестнадцатиричному
виду
******************************************************************************/
ULONG SPEED_SENS_GetState(void)
{
   /* запрещаем прерывание */
   GICR &= ~SPEED_SENSOR_ISR_ENABLE;

    /* считываем значения */
    register ULONG stop = Pulse.Time[Pulse.Start ^ 1];
    register ULONG start = Pulse.Time[Pulse.Start];

    /* разрешаем прерывание */
    GICR |= SPEED_SENSOR_ISR_ENABLE;

    /* считаем разность (STOP - START)*/
    stop = (LOWORD(stop) >> 1) /* преобразуем в мкс */
       * HIWORD(stop);
start = (LOWORD(start) >> 1) /* преобразуем в мкс */
       * HIWORD(start);

    return stop - start;
}


Правда, частота моего МК 16 МГц, так что в вашем случае младшее слово содержало бы просто значение микросекунд. У меня прескалер ТС1 настроен, как и у Вас на div 8, прерывание по совпадению TCCR1A (1999) со сбросом ТС1.

В главном цикле вызывается данная процедура, потом спокойно считается все что нам надо. Время выполнения прерывания менее 15 мкс (точно не считал)

Сообщение отредактировал prottoss - Nov 7 2006, 13:35


--------------------
Go to the top of the page
 
+Quote Post
singlskv
сообщение Nov 7 2006, 13:49
Сообщение #18


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



2 prottoss
Цитата(prottoss @ Nov 7 2006, 10:04) *
Код
#pragma vector = SPEED_SENSOR_ISR
__interrupt void SPEED_SENS_isr(void)
{
   /* запоминаем текущее время */
    Pulse.Time[Pulse.Start] = MAKEULONG(LOWORD(g_SysTIME_ms),TCNT1);

    /* формируем новый индекс для стартового времени */
    Pulse.Start ^= 1;
}

У Вас точно такая же проблемма в коде что и у автора топика.
Что будет если в этой строке MAKEULONG(LOWORD(g_SysTIME_ms),TCNT1) Вы
считаете значение TCNT1=0 ?
А означать это будет переполнение таймера, то есть Вы получите время на
1ms меньше чем реальное.

2 Alechin
Вы попробовали подправленный код из поста #10 ?
Go to the top of the page
 
+Quote Post
prottoss
сообщение Nov 7 2006, 14:06
Сообщение #19


Гуру
******

Группа: Свой
Сообщений: 2 720
Регистрация: 24-03-05
Пользователь №: 3 659



Цитата(singlskv @ Nov 7 2006, 20:49) *
2 prottoss
У Вас точно такая же проблемма в коде что и у автора топика.
Что будет если в этой строке MAKEULONG(LOWORD(g_SysTIME_ms),TCNT1) Вы
считаете значение TCNT1=0 ?
А означать это будет переполнение таймера, то есть Вы получите время на
1ms меньше чем реальное.
Да...на самом деле. Значит в функции, возвращающей значение периода необходимо добавить проверку младшего слова на значение ХХ, если меньше, то прибавлять к конечному результату еще одну 1000...


--------------------
Go to the top of the page
 
+Quote Post
singlskv
сообщение Nov 7 2006, 14:22
Сообщение #20


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(prottoss @ Nov 7 2006, 17:06) *
Цитата(singlskv @ Nov 7 2006, 20:49) *
2 prottoss
У Вас точно такая же проблемма в коде что и у автора топика.
Что будет если в этой строке MAKEULONG(LOWORD(g_SysTIME_ms),TCNT1) Вы
считаете значение TCNT1=0 ?
А означать это будет переполнение таймера, то есть Вы получите время на
1ms меньше чем реальное.
Да...на самом деле. Значит в функции, возвращающей значение периода необходимо добавить проверку младшего слова на значение ХХ, если меньше, то прибавлять к конечному результату еще одну 1000...

Нет, не в функции возвращающей значение периода, а в прерывании.
В Вашем случае это будет выглядеть примерно так:
Код
#pragma vector = SPEED_SENSOR_ISR
__interrupt void SPEED_SENS_isr(void)
{
   WORD lo = TCNT1;
   WORD hi = LOWORD(g_SysTIME_ms);

   if (((TIFR & (1 << OCF1A)) != 0)&&(lo<1000)) hi++; //  OCR1A/2 =1000

    Pulse.Time[Pulse.Start] = MAKEULONG(hi,lo);
    Pulse.Start ^= 1;
}
Go to the top of the page
 
+Quote Post
=GM=
сообщение Nov 7 2006, 14:24
Сообщение #21


Ambidexter
*****

Группа: Свой
Сообщений: 1 589
Регистрация: 22-06-06
Из: Oxford, UK
Пользователь №: 18 282



Цитата(Alechin @ Nov 7 2006, 12:34) *
У меня младшее слово счетчика системного времени инкрементируется аппаратно (это счетный регистр таймера). Соотв. накладные расходы на такой таймер - одно прерывание по переполнению таймера за 65536 мксек. Вот тут то и проблема - пол счетчика аппаратные, половина нет.


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

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


--------------------
Делай сразу хорошо, плохо само получится
Go to the top of the page
 
+Quote Post
singlskv
сообщение Nov 7 2006, 14:34
Сообщение #22


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(=GM= @ Nov 7 2006, 17:24) *
Попробуйте изменить приоритет прерываний. При входе в любое внешнее прерывание тотчас разрешайте глобальные прерывания, тогда, при наличии прерывания по переполнению таймера, оно будет обработано в первую очередь, и ваши программы обработки внешнего прерывания получат правильное системное время.

Да, это сильное решение a14.gif
Особенно красиво оно будет работать, когда одно внешнее прерывание будет
считывать двумя командами регистр TCNT , и между этими командами возникнет
другое внешнее прерывание blink.gif
Go to the top of the page
 
+Quote Post
prottoss
сообщение Nov 7 2006, 14:37
Сообщение #23


Гуру
******

Группа: Свой
Сообщений: 2 720
Регистрация: 24-03-05
Пользователь №: 3 659



Цитата(singlskv @ Nov 7 2006, 21:22) *
В Вашем случае это будет выглядеть примерно так:
Код
#pragma vector = SPEED_SENSOR_ISR
__interrupt void SPEED_SENS_isr(void)
{
   WORD lo = TCNT1;
   WORD hi = LOWORD(g_SysTIME_ms);

   if (((TIFR & (1 << OCF1A)) != 0)&&(lo<1000)) hi++; //  OCR1A/2 =1000

    Pulse.Time[Pulse.Start] = MAKEULONG(hi,lo);
    Pulse.Start ^= 1;
}


Да нет уж, тогда делать не так. Лучше уж сделать в прерывании три регистровых переменных и последовательно считать в них значение TC1 и TIFR, потом спокойно все это сохранить в память, а уж разгребать потом в процедуре GetState. По крайней мере можно будет знать сколько тактов ошибки - всего 2



Цитата(singlskv @ Nov 7 2006, 21:34) *
Цитата(=GM= @ Nov 7 2006, 17:24) *

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

Да, это сильное решение a14.gif
Особенно красиво оно будет работать, когда одно внешнее прерывание будет
считывать двумя командами регистр TCNT , и между этими командами возникнет
другое внешнее прерывание blink.gif
Да уж, решение...


--------------------
Go to the top of the page
 
+Quote Post
prottoss
сообщение Nov 7 2006, 14:48
Сообщение #24


Гуру
******

Группа: Свой
Сообщений: 2 720
Регистрация: 24-03-05
Пользователь №: 3 659



Может быть вот так?
Код
__interrupt void SPEED_SENS_isr(void)
{
   register UINT  tcnt1_reg = TCNT1;
   register UCHAR tifr_reg = TIFR;
      
   /* запоминаем текущее время */
    Pulse.Time[Pulse.Start] = MAKEULONG(LOWORD(g_SysTIME_ms),tcnt1_reg);
    Pulse.TIFR_reg = tifr_reg;

    /* формируем новый индекс для стартового времени */
    Pulse.Start ^= 1;
}

ну а в функции возврата периода импульса все считать...

Вот что получилось в Release:

Код
...

0000001C   B72F               IN      R18, 0x3F
    141             register UINT  tcnt1_reg = TCNT1;
   \   0000001E   B54C               IN      R20, 0x2C
   \   00000020   B55D               IN      R21, 0x2D
    142              register UCHAR tifr_reg = TIFR;
   \   00000022   B628               IN      R2, 0x38

...


--------------------
Go to the top of the page
 
+Quote Post
singlskv
сообщение Nov 7 2006, 14:56
Сообщение #25


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(prottoss @ Nov 7 2006, 17:37) *
Цитата(singlskv @ Nov 7 2006, 21:22) *
В Вашем случае это будет выглядеть примерно так:
Код
#pragma vector = SPEED_SENSOR_ISR
__interrupt void SPEED_SENS_isr(void)
{
   WORD lo = TCNT1;
   WORD hi = LOWORD(g_SysTIME_ms);

   if (((TIFR & (1 << OCF1A)) != 0)&&(lo<1000)) hi++; //  OCR1A/2 =1000

    Pulse.Time[Pulse.Start] = MAKEULONG(hi,lo);
    Pulse.Start ^= 1;
}


Да нет уж, тогда делать не так. Лучше уж сделать в прерывании три регистровых переменных и последовательно считать в них значение TC1 и TIFR, потом спокойно все это сохранить в память, а уж разгребать потом в процедуре GetState. По крайней мере можно будет знать сколько тактов ошибки - всего 2

Ну можно конечно TIFR и в память писать, это уже кому как удобно.

А количество тактов ошибки все равно непредсказуемо, например в ситуации
когда одновременно пришло несколько внешних прерываний.
Именно по этой причине идет сравнение (lo<1000).
Go to the top of the page
 
+Quote Post
prottoss
сообщение Nov 7 2006, 15:23
Сообщение #26


Гуру
******

Группа: Свой
Сообщений: 2 720
Регистрация: 24-03-05
Пользователь №: 3 659



Цитата(singlskv @ Nov 7 2006, 21:56) *
А количество тактов ошибки все равно непредсказуемо, например в ситуации
когда одновременно пришло несколько внешних прерываний.
Именно по этой причине идет сравнение (lo<1000).
Почему это не предсказуемо? У нас не предсказуемо значение софтверного слова счетчика до момента, пока мы не считали TCNT и пока мы не проанализировали OCF1A, потому что с периода входа в прерывание INT и до считывания TIFR счетчик мог переполнится, и, естественно, g_SysTIME_ms, тоже должно быть инкременированно. То что будет после считывания TIFR и TCNT1 нас уже не касается, это будущее состояние . То бишь OCF1A является 16-ым разрядом TCNT1 и нулевым разрядом g_SysTIME_ms. Считывая его и счетчик TCNT мы как раз и имеем полныое представление о программно-аппаратном счетчике. И ошибкf будет всего один такт TCNT1 (я ошибся в прошлом посте, сказав, что 2 такта)


--------------------
Go to the top of the page
 
+Quote Post
=GM=
сообщение Nov 7 2006, 15:36
Сообщение #27


Ambidexter
*****

Группа: Свой
Сообщений: 1 589
Регистрация: 22-06-06
Из: Oxford, UK
Пользователь №: 18 282



Цитата(singlskv @ Nov 7 2006, 14:34) *
Цитата(=GM= @ Nov 7 2006, 17:24) *

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

Да, это сильное решение a14.gif
Особенно красиво оно будет работать, когда одно внешнее прерывание будет считывать двумя командами регистр TCNT , и между этими командами возникнет другое внешнее прерывание blink.gif


Для автора важно, чтобы программа обработки внешних прерываний могла на лету получать корректное системное время и различие в 10-20 тактов считанного времени от текущего не имеет для него никакого значения на фоне 80000 тактов и более. Предлагаемое решение обеспечивает такую возможность, а также применимо для 8-ми прерываний.

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


--------------------
Делай сразу хорошо, плохо само получится
Go to the top of the page
 
+Quote Post
prottoss
сообщение Nov 7 2006, 16:10
Сообщение #28


Гуру
******

Группа: Свой
Сообщений: 2 720
Регистрация: 24-03-05
Пользователь №: 3 659



Продолжая свой пост, скажу, что счетчик TCNT1 инкременитруется за 8 тактов CPU и у меня, и у автора топика, то бишь, нам достаточно проанализировать флаг OCF1A, и по его установке инкрементировать старшее слово (g_SysTIME_ms). Единственное условие, при котором нам надо будет корректировать считанное значение TCNT1 - это если усановлен OCF1A и считанное значение TCNT1 = 1999 (в моем случае). Т.е на время считывания TCNT1 был равен 1999, а на момент считывания TIFR он инкрементировался


--------------------
Go to the top of the page
 
+Quote Post
singlskv
сообщение Nov 7 2006, 16:18
Сообщение #29


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(prottoss @ Nov 7 2006, 18:23) *
Цитата(singlskv @ Nov 7 2006, 21:56) *
А количество тактов ошибки все равно непредсказуемо, например в ситуации
когда одновременно пришло несколько внешних прерываний.
Именно по этой причине идет сравнение (lo<1000).
Почему это не предсказуемо? У нас не предсказуемо значение софтверного слова счетчика до момента, пока мы не считали TCNT и пока мы не проанализировали OCF1A, потому что с периода входа в прерывание INT и до считывания TIFR счетчик мог переполнится, и, естественно, g_SysTIME_ms, тоже должно быть инкременированно. То что будет после считывания TIFR и TCNT1 нас уже не касается, это будущее состояние . То бишь OCF1A является 16-ым разрядом TCNT1 и нулевым разрядом g_SysTIME_ms. Считывая его и счетчик TCNT мы как раз и имеем полныое представление о программно-аппаратном счетчике. И ошибкf будет всего один такт TCNT1 (я ошибся в прошлом посте, сказав, что 2 такта)

Представте себе что одновременно пришло 2 внешних прерывания.
Выполнятся начнет то, которое приоритетнее. А второе прерывание начнет выполняться
только после окончания первого, то есть для второго прерывания ошибка будет равна
времени работы первого прерывания + время входа во второе прерывание до момента
считывания TCNT.
Go to the top of the page
 
+Quote Post
prottoss
сообщение Nov 7 2006, 16:30
Сообщение #30


Гуру
******

Группа: Свой
Сообщений: 2 720
Регистрация: 24-03-05
Пользователь №: 3 659



Цитата(singlskv @ Nov 7 2006, 23:18) *
Представте себе что одновременно пришло 2 внешних прерывания. Выполнятся начнет то, которое приоритетнее. А второе прерывание начнет выполняться только после окончания первого, то есть для второго прерывания ошибка будет равна времени работы первого прерывания + время входа во второе прерывание до момента
считывания TCNT.
Если суммарное время прерываний больше периода счета счетчика TCNT1, тогда конечно - но это 1 МС! Конечно, если писать прерывания в стиле avr123 ), тада да. А если нет, что тогда? У нас счетчик считает в реалтайме, по этому каждое прерывание получит свое значения и только в одном из прерываний будет установлен 16-ый бит софтварного счетчика - OCF1A. Кроме того, что я это представлял, я еще моделировал различные ситуации с данной проблемой в AVRStudio...


--------------------
Go to the top of the page
 
+Quote Post

4 страниц V  < 1 2 3 4 >
Reply to this topicStart new topic
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 18th July 2025 - 02:59
Рейтинг@Mail.ru


Страница сгенерированна за 0.01528 секунд с 7
ELECTRONIX ©2004-2016