|
|
  |
Измерить частоту таймером at91sam7, Выходит что-то не то |
|
|
|
Jun 24 2008, 08:21
|
Участник

Группа: Новичок
Сообщений: 18
Регистрация: 20-01-05
Пользователь №: 2 078

|
День добрый! Подскажите, пожалуйста, где я не прав. at91sam7x256. Задумка: с помощью ТС0 в режиме capture измерять частоту со входа TIOA0 (частота порядка 1000 - 4000 Гц). Сам TC0 должен тактироваться MCK/128. Реально же получается, будто TC0 тактируется частотой, близкой к MCK/228 (судя по частоте с генератора и получаемым в *m_ValPtr значениям). Причём наблюдаю ещё один непонятный эффект: нелинейность измеренного значения частоты, процентов 10-15. Так инициализирую: Код // BSP_CPU_ClkFreq() даёт частоту MCK в кГц m_SomeCoeff = ( (BSP_CPU_ClkFreq() *1000) / 128); // Более-менее реальные показания удаётся получить, если 128 заменить на 228
m_TimerID = AT91C_ID_TC0; m_TC = AT91C_BASE_TC0;
//Timer configuration AT91C_BASE_PMC->PMC_PCER = (1 << m_TimerID); /* Enable the peripheral clk */ AT91C_BASE_TCB->TCB_TC0.TC_CCR = AT91C_TC_CLKDIS; /* TC0 timer disabled */ m_TC->TC_CMR = AT91C_TC_CLKS_TIMER_DIV4_CLOCK | // ~48MHz/128 AT91C_TC_ETRGEDG_FALLING | // Triggers on falling edge AT91C_TC_ABETRG | // Use TIOA0 as external trigger AT91C_TC_LDRA_RISING; // Load RA on RISING edge of TIOA0
AT91C_BASE_TCB->TCB_TC0.TC_CCR = AT91C_TC_CLKEN; /* TC0 timer enabled */ AT91C_BASE_TCB->TCB_TC0.TC_CCR = AT91C_TC_SWTRG; /* SWTRG to reset and start */
//AIC configuration AT91C_BASE_AIC->AIC_SVR[m_TimerID] = (INT32U)TachoSensor::ISR_Handler; AT91C_BASE_AIC->AIC_SMR[m_TimerID] = AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL | AT91C_AIC_PRIOR_LOWEST;
AT91C_BASE_AIC->AIC_ICCR = 1 << m_TimerID; AT91C_BASE_AIC->AIC_IECR = 1 << m_TimerID; m_TC->TC_IER = AT91C_TC_COVFS | //Enable Counter Overflow interrupt AT91C_TC_LDRAS;// | //Enable RA loading int.
m_TC->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; А так обрабатываю прерывания: Код void TachoSensor::ISR_Handler () { AT91C_BASE_AIC->AIC_IVR = 0;
unsigned int Status = m_TC->TC_SR;
if (Status & AT91C_TC_COVFS) { OvfCnt = true; *m_ValPtr = 0; // Считаем, что 0 (несущественная) }
if (Status & AT91C_TC_LDRAS) { m_PrevVal = m_CurrVal; m_CurrVal = AT91C_BASE_TC0->TC_RA;
if (OvfCnt) { OvfCnt = false; *m_ValPtr = 0; // Считаем, что 0 (несущественная) } else { if ( m_CurrVal != 0 ) { *m_ValPtr = m_SomeCoeff / m_CurrVal; } } }
AT91C_BASE_AIC->AIC_ICCR = 1 << m_TimerID; AT91C_BASE_AIC->AIC_EOICR = 0; /* Signal end of interrupt */
} /* TachoSensor::ISR_Handler */
|
|
|
|
|
Jun 24 2008, 09:24
|
Участник

Группа: Новичок
Сообщений: 18
Регистрация: 20-01-05
Пользователь №: 2 078

|
Цитата(aaarrr @ Jun 24 2008, 12:41)  Зачем руками запускаете, если external trigger настроен? Строчки Код AT91C_BASE_TCB->TCB_TC0.TC_CCR = AT91C_TC_CLKEN; /* TC0 timer enabled */ AT91C_BASE_TCB->TCB_TC0.TC_CCR = AT91C_TC_SWTRG; /* SWTRG to reset and start */ , похоже, лишние (т.к. AT91C_TC_CLKEN и AT91C_TC_SWTRG ещё раз прописываются в конце), но ведь плохо ручного запуска по AT91C_TC_SWTRG не должно быть? Цитата(aaarrr @ Jun 24 2008, 12:41)  Да, и Вы учитываете, что измеряется один полупериод? Полупериод чего? Я же из RA читаю, до какого значения досчитал таймер между передними фронтами импульсов на входе TIOA0. Или что-то не так? Код AT91C_TC_LDRA_RISING; // Load RA on RISING edge of TIOA0
|
|
|
|
|
Jun 24 2008, 10:08
|
Участник

Группа: Новичок
Сообщений: 18
Регистрация: 20-01-05
Пользователь №: 2 078

|
Цитата(aaarrr @ Jun 24 2008, 13:41)  Не так: по спаду TIOA0 таймер сбрасывается, а по фронту защелкивается новое значение. Хм, да, действительно. Тогда, кажется, понятно, и откуда взялась нелинейность. Осталось вспомнить, почему я так сделал и проверить более корректный вариант. Кстати, а если я буду и сброс и загрузку RA делать по одному и тому же фронту, то будет успевать загрузиться RA до сброса таймера? Там как оно реализовано, гонок не возникает? Цитата m_PrevVal Вы устанавливаете, но не используете. Да, в этом варианте не используется. Забыл убрать, когда постил.
|
|
|
|
|
Jun 24 2008, 10:54
|
Участник

Группа: Новичок
Сообщений: 18
Регистрация: 20-01-05
Пользователь №: 2 078

|
Цитата(aaarrr @ Jun 24 2008, 14:18)  По-идее, можно: таймер сбросится только на следующем такте входного клока. Запись в RA, небось, тем же клоком тактируется... Конечно, хотелось бы подтверждения в даташите, но, кажется, его там нет. И зачем-то ж я тогда разнёс фронты... Ладно, буду пробовать. Спасибо!
|
|
|
|
|
Jul 2 2008, 09:03
|
Участник

Группа: Новичок
Сообщений: 18
Регистрация: 20-01-05
Пользователь №: 2 078

|
После некоторой паузы опять вернулся к выправлению кода измерения частоты. Что я понял: да, действительно я так мерил именно пол периода. Делалось это по той причине, что если я настраиваю оба действия (триггер и запись в RA) по одному и тому же фронту, то реально таймер отсчитывает и пишет в RA то правильное кол-во импульсов, то за пол периода, притом чаще именно за пол периода. Почему - не понял до сих пор. И, кроме того, похоже, что иногда вообще считывается 0, т.е., как будто TC успевает сброситься раньше, чем записаться RA... Чтобы уйти от этих неопределённостей я настроил триггер TIOA и запись в RA по ОБОИМ фронтам, и отсчитанные полупериоды просто складываю. Осталась неопределённость с нулём. Я пока не придумал ничего лучшего, как просто игнорировать нулевые значения. Вот, собственно, так теперь оно работает. Инициализация: Код TachoSensor::TachoSensor (char *Name, char TimerNumber) : BaseSensor(Name, 0, Parameters.SpeedLimitHard * 3) { m_SomeCoeff = ( (BSP_CPU_ClkFreq() * 1000) >> 7);
m_TimerID = AT91C_ID_TC0; m_TC = AT91C_BASE_TC0;
m_PrevVal = 0; m_CurrVal = 0;
//Timer configuration AT91C_BASE_PMC->PMC_PCER = (1 << m_TimerID); /* Enable the peripheral clk */ AT91C_BASE_TCB->TCB_TC0.TC_CCR = AT91C_TC_CLKDIS; /* TC0 timer disabled */ /* Конфигурим срабатывание по обоим фронтам, т.к. он всё равно почему-то часто срабатывает по обоим и в других режимах. */ m_TC->TC_CMR = AT91C_TC_CLKS_TIMER_DIV4_CLOCK | // ~48MHz/128 AT91C_TC_ETRGEDG_BOTH | // Triggers on both edge AT91C_TC_ABETRG | // Use TIOA0 as external trigger AT91C_TC_LDRA_BOTH; // Load RA on both edge of TIOA0
//AIC configuration AT91C_BASE_AIC->AIC_SVR[m_TimerID] = (INT32U)TachoSensor::ISR_Handler; AT91C_BASE_AIC->AIC_SMR[m_TimerID] = AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL | AT91C_AIC_PRIOR_LOWEST;
AT91C_BASE_AIC->AIC_ICCR = 1 << m_TimerID; AT91C_BASE_AIC->AIC_IECR = 1 << m_TimerID; m_TC->TC_IER = AT91C_TC_COVFS | //Enable Counter Overflow interrupt AT91C_TC_LDRAS;// | //Enable RA loading int. // AT91C_TC_ETRGS; //Enable ext. trigger int.
AT91C_BASE_TCB->TCB_TC0.TC_CCR = AT91C_TC_CLKEN; /* TC0 timer enabled */
} /* TachoSensor::TachoSensor */ Обработчик: Код void TachoSensor::ISR_Handler () { AT91C_BASE_AIC->AIC_IVR = 0; /* Write the IVR, as required in Protection Mode */ unsigned int Status = m_TC->TC_SR;
if (Status & AT91C_TC_COVFS) { OvfCnt = true; *m_ValPtr = 0; // Считаем, что 0 (несущественная) }
if (Status & AT91C_TC_LDRAS) { if (m_SemiPeriod) { m_SemiVal1 = AT91C_BASE_TC0->TC_RA; m_SemiPeriod = false; } else { m_SemiVal2 = AT91C_BASE_TC0->TC_RA; m_SemiPeriod = true; } m_PrevVal = m_CurrVal;
if (m_SemiVal1 && m_SemiVal2) // Почему-то иногда из TC_RA считывается 0, такие значения игнорируем. m_CurrVal = m_SemiVal1 + m_SemiVal2;
if (OvfCnt) { OvfCnt = false; { *m_ValPtr = 0; // Считаем, что 0 (несущественная) } } else { if (m_CurrVal != 0) { *m_ValPtr = m_SomeCoeff / m_CurrVal; } } }
AT91C_BASE_AIC->AIC_ICCR = 1 << m_TimerID; AT91C_BASE_AIC->AIC_EOICR = 0; /* Signal end of interrupt */
} /* TachoSensor::ISR_Handler */
|
|
|
|
|
Jul 2 2008, 10:07
|
Участник

Группа: Новичок
Сообщений: 18
Регистрация: 20-01-05
Пользователь №: 2 078

|
Цитата(aaarrr @ Jul 2 2008, 13:37)  А с входным сигналом все в порядке, дребезга нет? С генератора меандр, через каскад на КТ315 и оптопару. То же самое в реальных условиях с датчиком Холла.
|
|
|
|
|
Jul 2 2008, 12:14
|
Участник

Группа: Новичок
Сообщений: 18
Регистрация: 20-01-05
Пользователь №: 2 078

|
Цитата(aaarrr @ Jul 2 2008, 14:41)  Фронты нормальные, не завалены? Вообще-то не обратил внимания. Но явно моего внимания не привлекли, когда смотрел. Это с генератора. С датчика осциллом не смотрел, а по данным даташита датчика - Switching Time Rise (10 % to 90 %) 15 µs max. Switching Time Fall (90 % to 10 %) 1.0 µs max. Но, конечно, там ещё метров 8 витой пары.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|