Перейти к содержанию
    

real time timer - прерывание по переполнению

aaarrr, ок. спасибо за желание помочь.

 

template <uint_t i_timer>
class CTimerImpl
{
private:
 static const AT91PS_TC  m_timer;
 static const uint_t   m_timer_id = 
   i_timer == 0 ? AT91C_ID_TC0 : 
   i_timer == 1 ? AT91C_ID_TC1 : 
   i_timer == 2 ? AT91C_ID_TC2 : 0;

private:
 static const uint32_t m_max_ticks = 0x10000;
 static const uint32_t m_interrupt_priority = 1;

public:
 static CTimerImpl<i_timer>* m_instance;

private:
 float   m_max_time_ms;
 float   m_ticks_per_ms;

private:
 static void interrupt_handler()
 {
   uint32_t mask = m_timer->TC_SR;
   uint32_t x = 0;
   x = mask;
   // m_instance->on_interrupt(mask);
 }

 inline bool on_interrupt(uint32_t mask)
 {
   if (mask & AT91C_TC_CPAS)
     on_event_a();
   if (mask & AT91C_TC_CPBS)
     on_event_b();
   if (mask & AT91C_TC_CPCS)
     on_overflow();

   return (mask & (AT91C_TC_CPAS | AT91C_TC_CPCS | AT91C_TC_CPBS)) != 0;
 }

 uint32_t calculate_optimal_divider(float max_period_ms)
 {
   const float cycles_per_ms = CSystem::get_instance()->get_frequency() / 1000.0f;
   const float max_time_ms = m_max_ticks / cycles_per_ms;

   m_max_time_ms = max_time_ms * 2.0f;
   m_ticks_per_ms = cycles_per_ms / 2.0f;

   if (m_max_time_ms > max_period_ms)
     return AT91C_TC_CLKS_TIMER_DIV1_CLOCK;

   m_max_time_ms = max_time_ms * 8.0f;
   m_ticks_per_ms = cycles_per_ms / 8.0f;

   if (m_max_time_ms > max_period_ms)
     return AT91C_TC_CLKS_TIMER_DIV2_CLOCK;

   m_max_time_ms = max_time_ms * 32.0f;
   m_ticks_per_ms = cycles_per_ms / 32.0f;

   if (m_max_time_ms > max_period_ms)
     return AT91C_TC_CLKS_TIMER_DIV3_CLOCK;

   m_max_time_ms = max_time_ms * 128.0f;
   m_ticks_per_ms = cycles_per_ms / 128.0f;

   if (m_max_time_ms > max_period_ms)
     return AT91C_TC_CLKS_TIMER_DIV4_CLOCK;

   m_max_time_ms = max_time_ms * 1024.0f;
   m_ticks_per_ms = cycles_per_ms / 1024.0f;

   if (m_max_time_ms > max_period_ms)
     return AT91C_TC_CLKS_TIMER_DIV5_CLOCK;

   __STL_THROW(invalid_argument("vary big period"));
   return AT91C_TC_CLKS_TIMER_DIV5_CLOCK;
 }

protected:
 virtual void on_event_a() = 0;
 virtual void on_event_b() = 0;
 virtual void on_overflow() = 0;

 void initialize(float max_time_ms)
 {
   m_instance = this;

   AT91F_AIC_CfgPMC();
   AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC, 1 << m_timer_id);

   // Disable TC clock
   m_timer->TC_CCR = AT91C_TC_CLKDIS;
   AT91F_TC_InterruptDisable(m_timer, 0xFFFFFFFF);

   // Clear status register
   uint32_t mask = m_timer->TC_SR;
   mask = ~mask;

   // Set mode
   m_timer->TC_CMR = calculate_optimal_divider(max_time_ms) | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_WAVE | AT91C_TC_EEVT_XC0;

   // enable interrupts 
   AT91F_TC_InterruptEnable(m_timer, AT91C_TC_CPCS | AT91C_TC_CPAS | AT91C_TC_CPBS);

   // configure interrupt handler
   AT91F_AIC_ConfigureIt(
     AT91C_BASE_AIC, 
     m_timer_id, 
     m_interrupt_priority, 
     AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, 
     (void(*)())interrupt_handler
   );

   // enable interrupt
   AT91F_AIC_EnableIt(AT91C_BASE_AIC, m_timer_id);
 }

 void deinitialize()
 {
   stop();

   // disable interrupts
   AT91F_TC_InterruptDisable(m_timer, AT91C_TC_CPCS | AT91C_TC_CPAS | AT91C_TC_CPBS);

   // mode
   m_timer->TC_CMR = 0;

   // perepheral clock
   AT91F_PMC_DisablePeriphClock(AT91C_BASE_PMC, 1 << m_timer_id);
 }

 void set_time_a(float time_a_ms)
 {
   m_timer->TC_RA = (uint32_t)(std_math::min(time_a_ms, m_max_time_ms) * m_ticks_per_ms);
 }

 void set_time_b(float time_b_ms)
 {
   m_timer->TC_RB = (uint32_t)(std_math::min(time_b_ms, m_max_time_ms) * m_ticks_per_ms);
 }

 void set_time_reset(float period_ms)
 {
   m_timer->TC_RC = (uint32_t)(std_math::min(period_ms, m_max_time_ms) * m_ticks_per_ms);
 }

 inline void start()
 {
   m_timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
 }

 inline void stop()
 {
   m_timer->TC_CCR = AT91C_TC_CLKDIS;
 }
};

template <>
const AT91PS_TC CTimerImpl<0>::m_timer = AT91C_BASE_TC0;

template <>
const AT91PS_TC CTimerImpl<1>::m_timer = AT91C_BASE_TC1;

template <>
const AT91PS_TC CTimerImpl<2>::m_timer = AT91C_BASE_TC2;

template <uint_t i_timer>
CTimerImpl<i_timer>* CTimerImpl<i_timer>::m_instance;

 

вызываю так:

    CTimer<0> timer(10.0f);

    timer.set_time_a(2.0f);
    timer.set_time_b(6.0f);
    timer.set_time_reset(8.0f);

    timer.start();

    while(1);

Изменено пользователем aaarrr
Оформление цитаты исходника

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

в симуляторе Keil всё пучком :07:

максимально упростил код. кому не лень, пожалуйста, скомпилируйте у себя и посмотрите чему равен mask. может у меня контроллер косячный...

 

void timer_handler()
{
  unsigned int mask = AT91C_BASE_TC0->TC_SR;
  mask ++;
}

int main()
{
  // enable PMC
  AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_FIQ;
  AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_TC0;

  // set mode
  AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS;
  AT91C_BASE_TC0->TC_IDR = 0xFFFFFFFF;
  AT91C_BASE_TC0->TC_SR;
  AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV5_CLOCK | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_WAVE | AT91C_TC_EEVT_XC0;

  // enable interrupts on compare A, B, C
  AT91C_BASE_TC0->TC_IER = AT91C_TC_CPAS | AT91C_TC_CPBS | AT91C_TC_CPCS;
  AT91C_BASE_TC0->TC_RA = 100;
  AT91C_BASE_TC0->TC_RB = 200;
  AT91C_BASE_TC0->TC_RC = 400;

  // start
  AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;

  // set irq handler
  AT91C_BASE_AIC->AIC_IDCR = 1 << AT91C_ID_TC0;
  AT91C_BASE_AIC->AIC_SMR[AT91C_ID_TC0] = 0;
  AT91C_BASE_AIC->AIC_SVR[AT91C_ID_TC0] = (unsigned int)timer_handler;
  AT91C_BASE_AIC->AIC_ICCR = 1 << AT91C_ID_TC0;

  // enable interrupts from timer
  AT91C_BASE_AIC->AIC_IECR = 1 << AT91C_ID_TC0;

  while (1);
}

Изменено пользователем srm

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вроде, немного разобрался. Если поставить значение делителя 1024 и значения регистров A ~ 10000, B ~ 40000, C ~ 60000, то ошибка перестаёт повторяться.

 

Я несколько изменил тестовый пример. в обработчике делаю так:

volatile static int ss;

void timer_handler()
{
  unsigned int mask = AT91C_BASE_TC0->TC_SR;
  ss = (mask & (AT91C_TC_CPAS | AT91C_TC_CPBS | AT91C_TC_CPCS)) == (AT91C_TC_CPAS | AT91C_TC_CPBS);
}

А в программе постоянно вывожу значение ss:

  while (1)
  {
    printf("%d", ss);
  }

 

Если делитель = 32, A = 0x7331, B = 0xA681, С = 0xD335. Т.о. Время между двумя сравнениями 13136*32/48000 = 8,75 мс. печатается:

00000100001000001000000100000100000100000010000010000010000010000010000001000001

0000010000010000001000001000001

ну не может прерывание обрабатываться 8 миллисекунд! тем более, что при уменьшении времени (вплодь до десятых долей мс) выводится примерно то же самое.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В реалтайме, вроде, всё норм. У меня 12 сервомеханизмов, шимы под них генерирует таймер at91sam7s256. Всё замечательно, сервы крутятся именно так, как мне нужно. Но, опять всплыл подводный камень и опять с real time timer'ом.

 

если я в цикле изменяю позицию серв:

  while (true)
  {
    for (uint32_t x = 0; x < 20000; x ++)
    {
      servo0.set_position(servo::min_position + x * (servo::max_position - servo::min_position) / 20000.0f);
      servo2.set_position(servo::min_position + x * (servo::max_position - servo::min_position) / 20000.0f);
      // ...
      servo11.set_position(servo::min_position + x * (servo::max_position - servo::min_position) / 20000.0f);
    }

    for (uint32_t x = 0; x < 20000; x ++)
    {
      servo0.set_position(servo::max_position - x * (servo::max_position - servo::min_position) / 20000.0f);
      servo2.set_position(servo::max_position - x * (servo::max_position - servo::min_position) / 20000.0f);
      // ...
      servo11.set_position(servo::max_position - x * (servo::max_position - servo::min_position) / 20000.0f);
    }
  }

, то всё прекрасно. Если я синхронизирую позицию по таймеру, то сервы время от времени начинают уезжать. т.е. двигается медленно, с постоянной скоростью в одну сторону, потом раз - и резко ушла в другую, потом снова медленно двигается вперёд. повторяется, примерно, один раз за 4 прохода цикла (20 сек):

  while (true)
  {
    start_time = watch->get_time_us();
    
    while (true)
    {
      uint64_t current_time = watch->get_time_us();
  
      if (current_time - start_time > 5000000)
        break;
  
      float x = (current_time - start_time) / 5000000.0f;
      servo0.set_position(servo::min_position + x * (servo::max_position - servo::min_position));
    }

    start_time = watch->get_time_us();
    
    while (true)
    {
      uint64_t current_time = watch->get_time_us();
  
      if (current_time - start_time > 5000000)
        break;
  
      float x = (current_time - start_time) / 5000000.0f;
      servo0.set_position(servo::max_position - x * (servo::max_position - servo::min_position));
    }
  }

Изменено пользователем srm

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну а get_time_us() что собой представляет?

 

Желание использовать RTT как источник информации о времени представляется, мягко говоря, несколько странным: таймер работает от RC-генератора, частота которого по определению может бултыхаться в широких пределах. Единственное разумное его применение - это поднимать процессор из idle по будильнику.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

aaarrr, я, вроде, по применению и использую. насколько я понял из даташита, real time timer служит как раз для получения информации о текущем времени. Тем более, что большая точность мне не нужна. Время мне нужно для того, чтобы движения робота были синхронны. Ну и плюс для работы ПИД регуляторов.

get_time_us всего лишь запрашивает показание счётчика и переводит тики в микросекунды.

По ходу дела, все эти глюки происходят из-за jtag'a. если запускать программу на выполнение без отладки, при отключенном Jtag'е, то глюки, вроде, не повторяются.. В общем, magic!

Изменено пользователем srm

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

aaarrr, я, вроде, по применению и использую. насколько я понял из даташита, real time timer служит как раз для получения информации о текущем времени. get_time_us всего лишь запрашивает показание счётчика и переводит тики в микросекунды.

RC Oscillator Frequency: Min. 22 Typ. 32 Max. 42 kHz

Т.е. плюс-минус лапоть. Устраивает такое время?

 

Для формирования точных интервалов существует Periodic Interval Timer.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

мдя... робот будет ходить либо вдвое быстрее, либо вдвое медленнее :))))))

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

мдя... робот будет ходить либо вдвое быстрее, либо вдвое медленнее :))))))

Вот именно. Тут не 5%, а совсем неприлично.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Добрый день. Дабы не плодить похожих тем решил спросить здесь.

Пытаюсь вызвать прерывание от RTTC. Работаю в Keil. Инициализирую так:

AT91S_AIC  *pAIC = AT91C_BASE_AIC;
void INIT_RTTC (int Alarm_Value_seconds) {                       /* Initialize Real Time Timer */
   pRTTC->RTTC_RTAR = (Alarm_Value_seconds - 1);
   pAIC->AIC_IECR = AT91C_ID_SYS;
   pAIC->AIC_SMR[1] = AT91C_AIC_PRIOR_HIGHEST | AT91C_AIC_SRCTYPE_POSITIVE_EDGE; 
   pRTTC->RTTC_RTMR = (AT91C_RTTC_RTPRES & 0x8000) | AT91C_RTTC_ALMIEN | AT91C_RTTC_RTTRST; 
}

Обработчик объявляю так:

__irq void RTTC_Interrupt_Handler (void) {

        if ((pRTTC->RTTC_RTSR & AT91C_RTTC_ALMS) != 0) {
              LED_ON;
        } else {
              LED_OFF;
        }

}

 

В стартапе этот кусок поменял так:

Reset_Addr      DCD     Reset_Handler
Undef_Addr      DCD     Undef_Handler
SWI_Addr        DCD     SWI_Handler
PAbt_Addr       DCD     PAbt_Handler
DAbt_Addr       DCD     DAbt_Handler
                DCD     0        ; Reserved Address
IRQ_Addr        DCD     IRQ_Handler
FIQ_Addr        DCD     FIQ_Handler

Undef_Handler   B       Undef_Handler
SWI_Handler     B       SWI_Handler
PAbt_Handler    B       PAbt_Handler
DAbt_Handler    B       DAbt_Handler
                   PRESERVE8
                   IMPORT RTTC_Interrupt_Handler
IRQ_Handler     B       RTTC_Interrupt_Handler
FIQ_Handler     B       FIQ_Handler

 

В общем, прерывание не происходит. В чем ошибка, подскажите, пожалуйста.

P.S. Отладчиком смотрю содержимое регистра RTT_SR - флаги ALMS и RTCINC -поднимаются и SYSIRQ - находится в состоянии pending. Однако, перехода по вектору не происходит...

Изменено пользователем Nikitoc

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

AT91C_AIC_SRCTYPE_POSITIVE_EDGE

Ну почему, почему все упорно пытаются работать по фронту, а? Для внутренних источников это в 99.9% случаев не нужно, для внешних - в 90%. В остальных 0.1% и 10% нужно проявлять предельную внимательность при обслуживании такого прерывания. В вашем случае отсутствует сброс источника в AIC, и запись в EOICR (хотя если принять во внимание переделку в стартапе, последнее уже несущественно).

 

А уж прерывания RTT - это просто одно большое недоразумение. Для их использования нужно иметь ну очень веские основания.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну почему, почему все упорно пытаются работать по фронту, а? Для внутренних источников это в 99.9% случаев не нужно, для внешних - в 90%. В остальных 0.1% и 10% нужно проявлять предельную внимательность при обслуживании такого прерывания. В вашем случае отсутствует сброс источника в AIC, и запись в EOICR (хотя если принять во внимание переделку в стартапе, последнее уже несущественно).

 

А уж прерывания RTT - это просто одно большое недоразумение. Для их использования нужно иметь ну очень веские основания.

 

Изменил инициализацию таким образом:

void INIT_RTTC (int Alarm_Value_seconds) { /* Initialize Real Time Timer */

pRTTC->RTTC_RTAR = (Alarm_Value_seconds - 1);

pAIC->AIC_IECR = AT91C_ID_SYS;

pAIC->AIC_SMR[1] = AT91C_AIC_PRIOR_HIGHEST;

pAIC->AIC_ICCR = AT91C_ID_SYS;

pRTTC->RTTC_RTMR = (AT91C_RTTC_RTPRES & 0x8000) | AT91C_RTTC_ALMIEN | AT91C_RTTC_RTTRST;

 

А обработчик так:

__irq void RTTC_Interrupt_Handler (void) {

 

if ((pRTTC->RTTC_RTSR & AT91C_RTTC_ALMS) != 0) {

LED_ON;

} else {

LED_OFF;

}

pAIC->AIC_EOICR = 0; // Reset AIC logic

}

Все равно не заходит! :crying:

Изменено пользователем Nikitoc

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Изменил инициализацию таким образом:

ICCR работает только для прерываний по фронту, писать его здесь бессмысленно.

 

А обработчик так:

EOICR нужно писать в любом случае, если уж оказались в прерывании, а не по каким-либо еще условиям.

 

Глобально прерывания разрешены? Отладчик малину не портит, часом?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Глобально прерывания разрешены?

Ээээмммм.... А где они разрешаются? :blush:

Изменено пользователем Nikitoc

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Разрешаются сбросом бита I регистра CPSR процессора. Из-под 'C' обычно предусматривается доступ в виде intrinsic-функций типа __enable_inerttupts(), или чего-то подобного - смотрите хелп своего компилятора.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...