|
Как считать значение аппаратно-программного таймера... |
|
|
|
 |
Ответов
(15 - 29)
|
Nov 7 2006, 12:34
|
Частый гость
 
Группа: Свой
Сообщений: 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 мксек. Вот тут то и проблема - пол счетчика аппаратные, половина нет.
|
|
|
|
|
Nov 7 2006, 12:54
|

Гуру
     
Группа: Свой
Сообщений: 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
--------------------
|
|
|
|
|
Nov 7 2006, 13:49
|
дятел
    
Группа: Свой
Сообщений: 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 ?
|
|
|
|
|
Nov 7 2006, 14:22
|
дятел
    
Группа: Свой
Сообщений: 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; }
|
|
|
|
|
Nov 7 2006, 14:24
|

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

|
Цитата(Alechin @ Nov 7 2006, 12:34)  У меня младшее слово счетчика системного времени инкрементируется аппаратно (это счетный регистр таймера). Соотв. накладные расходы на такой таймер - одно прерывание по переполнению таймера за 65536 мксек. Вот тут то и проблема - пол счетчика аппаратные, половина нет. Насколько я понял, ваша проблема состоит в том, что одновременно с возникновением прерывания по переполнению таймера может возникнуть несколько из 4-х внешних прерываний, и затем, когда внешнее прерывание пытается считать системное время, оно еще не скорректировано в соответствующем прерывании, следовательно, возникает ошибка в 65 мс. Попробуйте изменить приоритет прерываний. При входе в любое внешнее прерывание тотчас разрешайте глобальные прерывания, тогда, при наличии прерывания по переполнению таймера, оно будет обработано в первую очередь, и ваши программы обработки внешнего прерывания получат правильное системное время. Временные рамки у вас огромные (1мс-2000мс), должно работать.
--------------------
Делай сразу хорошо, плохо само получится
|
|
|
|
|
Nov 7 2006, 14:34
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

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

Гуру
     
Группа: Свой
Сообщений: 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)  Попробуйте изменить приоритет прерываний. При входе в любое внешнее прерывание тотчас разрешайте глобальные прерывания, тогда, при наличии прерывания по переполнению таймера, оно будет обработано в первую очередь, и ваши программы обработки внешнего прерывания получат правильное системное время.
Да, это сильное решение Особенно красиво оно будет работать, когда одно внешнее прерывание будет считывать двумя командами регистр TCNT , и между этими командами возникнет другое внешнее прерывание Да уж, решение...
--------------------
|
|
|
|
|
Nov 7 2006, 14:48
|

Гуру
     
Группа: Свой
Сообщений: 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
...
--------------------
|
|
|
|
|
Nov 7 2006, 14:56
|
дятел
    
Группа: Свой
Сообщений: 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).
|
|
|
|
|
Nov 7 2006, 15:23
|

Гуру
     
Группа: Свой
Сообщений: 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 такта)
--------------------
|
|
|
|
|
Nov 7 2006, 15:36
|

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

|
Цитата(singlskv @ Nov 7 2006, 14:34)  Цитата(=GM= @ Nov 7 2006, 17:24)  Попробуйте изменить приоритет прерываний. При входе в любое внешнее прерывание тотчас разрешайте глобальные прерывания, тогда, при наличии прерывания по переполнению таймера, оно будет обработано в первую очередь, и ваши программы обработки внешнего прерывания получат правильное системное время.
Да, это сильное решение Особенно красиво оно будет работать, когда одно внешнее прерывание будет считывать двумя командами регистр TCNT , и между этими командами возникнет другое внешнее прерывание  Для автора важно, чтобы программа обработки внешних прерываний могла на лету получать корректное системное время и различие в 10-20 тактов считанного времени от текущего не имеет для него никакого значения на фоне 80000 тактов и более. Предлагаемое решение обеспечивает такую возможность, а также применимо для 8-ми прерываний. Другое дело, что для критических секций кода (например, при чтении двух старших байт системного времени или для вышеупомянутого случая) нужно запрещать все прерывания. Тут вы совершенно правы.
--------------------
Делай сразу хорошо, плохо само получится
|
|
|
|
|
Nov 7 2006, 16:18
|
дятел
    
Группа: Свой
Сообщений: 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.
|
|
|
|
|
Nov 7 2006, 16:30
|

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

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