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

 
 
> Как считать значение аппаратно-программного таймера...
Alechin
сообщение Nov 3 2006, 10:50
Сообщение #1


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

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



Есть некий системный таймер, построенный на аппаратном таймере контроллера и расширенный его программным счетчиком (по переполнению таймера) (пример - 32-ух разрядный счетчик микросекунд системного времени). Задача - чтение на лету его значения. Пока без остановки таймера это сделать у меня не получается (периодически получалась рассинхронизация переполнения счетчика аппаратного таймера и еще не инкрементированного старшего слова).
Но при останове таймера происходит потеря точности счета. Приходится корректировать значение. Т.е. все довольно сложно. Какие еще могут быть варианты?
Go to the top of the page
 
+Quote Post
4 страниц V  < 1 2 3 4 >  
Start new topic
Ответов (15 - 29)
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
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


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


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