Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Измерить частоту таймером at91sam7
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Glide
День добрый! Подскажите, пожалуйста, где я не прав. 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 */
aaarrr
Цитата(Glide @ Jun 24 2008, 12:21) *
Код
    AT91C_BASE_TCB->TCB_TC0.TC_CCR       =  AT91C_TC_SWTRG;          /* SWTRG to reset and start */
...
    m_TC->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;

Зачем руками запускаете, если external trigger настроен?

Да, и Вы учитываете, что измеряется один полупериод?
Glide
Цитата(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
aaarrr
Не так: по спаду TIOA0 таймер сбрасывается, а по фронту защелкивается новое значение. m_PrevVal Вы устанавливаете, но не используете.
Glide
Цитата(aaarrr @ Jun 24 2008, 13:41) *
Не так: по спаду TIOA0 таймер сбрасывается, а по фронту защелкивается новое значение.

Хм, да, действительно. Тогда, кажется, понятно, и откуда взялась нелинейность. Осталось вспомнить, почему я так сделал и проверить более корректный вариант.
Кстати, а если я буду и сброс и загрузку RA делать по одному и тому же фронту, то будет успевать загрузиться RA до сброса таймера? Там как оно реализовано, гонок не возникает?
Цитата
m_PrevVal Вы устанавливаете, но не используете.

Да, в этом варианте не используется. Забыл убрать, когда постил.
aaarrr
Цитата(Glide @ Jun 24 2008, 14:08) *
Кстати, а если я буду и сброс и загрузку RA делать по одному и тому же фронту, то будет успевать загрузиться RA до сброса таймера? Там как оно реализовано, гонок не возникает?

По-идее, можно: таймер сбросится только на следующем такте входного клока.
Glide
Цитата(aaarrr @ Jun 24 2008, 14:18) *
По-идее, можно: таймер сбросится только на следующем такте входного клока.

Запись в RA, небось, тем же клоком тактируется... Конечно, хотелось бы подтверждения в даташите, но, кажется, его там нет. И зачем-то ж я тогда разнёс фронты... Ладно, буду пробовать. Спасибо!
aaarrr
Тем же, только она должна случиться на такт раньше сброса. Впрочем, специальных указаний в DS действительно нет, так что лучше проверить на практике.
Glide
После некоторой паузы опять вернулся к выправлению кода измерения частоты. Что я понял: да, действительно я так мерил именно пол периода. Делалось это по той причине, что если я настраиваю оба действия (триггер и запись в 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 */
aaarrr
А с входным сигналом все в порядке, дребезга нет?
Glide
Цитата(aaarrr @ Jul 2 2008, 13:37) *
А с входным сигналом все в порядке, дребезга нет?

С генератора меандр, через каскад на КТ315 и оптопару. То же самое в реальных условиях с датчиком Холла.
aaarrr
Фронты нормальные, не завалены?
Glide
Цитата(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 витой пары.
aaarrr
Триггеров Шмитта у SAM'а на входах нет. И ловить он в такой ситуации - с фронтами на много мкс - может что угодно.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.