Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: real time timer - прерывание по переполнению
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > ARM, 32bit
Страницы: 1, 2
srm
Не нашёл как установить обработчик прерывания по переполнению для таймера реального времени. Нужно ведь как-то так:
Код
AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, ???, m_interrupt_priority, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, (void(*)())interrupt_handler);

А я не знаю какой у него ID..
srm
Забыл указать контроллер. at91sam7s256.
Можно ли вообще зарегистрировать обработчик на переполнение RTTC таймера?
injen-d
Таймер RTT входит в системную периферию (идентификатор: AT91C_ID_SYS).
должно быть примерно так:AIC_ConfigureIT(AT91C_ID_SYS, AT91C_AIC_PRIOR_HIGHEST | AT91C_AIC_SRCTYPE_POSITIVE_EDGE, SYS_handler),
где функция void SYS_handler(void) - обработчик прерывания от системной периферии, и уже в нем Вы должны выяснить, что именно вызвало данное прерывание: RTT или/и PIT или/и PMC или/и DBGU и т.д.
srm
injen-d, спасибо.
aaarrr
Еще обратите внимание, что что флаги RTT сбрасываются через 2 периода SCLK (~61us) после чтения статусного регистра. Поэтому попасть в прерывание просто, а вот выйти из него - не очень sad.gif
srm
Столкнулся с такой проблемой. Я поставил делитель 3. Это примерно 10 тактов за мс. Если запрашивать показания таймера на больших временах (>10 ms), то всё правильно (разница тиков соответствует прошедшему времени). Но если запрашивать показания через ~1ms, то значения получаются одинаковыми. Почему так происходит, ведь таймер за 1мс должен протикать 10 раз?

Причём, интересная особенность. Если сделать так:
Код
  uint32_t x = AT91F_RTTReadValue(AT91C_BASE_RTTC);
  for (volatile uint32_t i = 0; i < 1000; i ++);
  x = AT91F_RTTReadValue(AT91C_BASE_RTTC) - x;

то всё правильно. Если же вызывать AT91F_RTTReadValue(AT91C_BASE_RTTC) по таймеру, то разность получается 0.
aaarrr
Цитата(srm @ May 26 2010, 17:45) *
Если же вызывать AT91F_RTTReadValue(AT91C_BASE_RTTC) по таймеру, то разность получается 0.

Тогда покажите, как вы это делаете по таймеру.
srm
Локализовал проблему. Вот обработчик прерывания:
Код
  static void interrupt_handler()
  {
    uint32_t mask = m_timer->TC_SR;
    m_instance->on_interrupt(mask);
    AT91C_BASE_AIC->AIC_EOICR = 0;
  }

  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;
  }

Почему-то mask при каждом срабатывании содержит биты AT91C_TC_CPAS | AT91C_TC_CPBS (хотя значения регистров А = 0x2EED, B = 0x8CC9, C = 0xBBB6). Поэтому при каждом срабатывании вызываются оба метода:
Код
  virtual void on_event_a()
  {
    m_time = CWatch::get_instance()->get_time_us();
  }

  virtual void on_event_b()
  {
    m_time = CWatch::get_instance()->get_time_us() - m_time;
    m_time ++;
  }

почему регистр статуса не чистится?
--------------------------------------------------
Что-то с форматом вызова.. если __irq static void interrupt_handler() - вроде, правильно, работает. Только почему-то через некоторое время после старта выполнение уходит в UndefinedHandler. Мой обработчик перестаёт вызываться. IRQ стек не переполнен...
aaarrr
Цитата(srm @ May 26 2010, 18:17) *
Почему-то mask при каждом срабатывании содержит биты AT91C_TC_CPAS | AT91C_TC_CPBS (хотя значения регистров А = 0x2EED, B = 0x8CC9, C = 0xBBB6).

Может, просто прерывание по CPA забыли разрешить?

Цитата(srm @ May 26 2010, 18:17) *
Что-то с форматом вызова.. если __irq static void interrupt_handler() - вроде, правильно, работает. Только почему-то через некоторое время после старта выполнение уходит в UndefinedHandler. Мой обработчик перестаёт вызываться. IRQ стек не переполнен...

Как обрабатывается вектор прерывания в стартапе?
srm
Цитата
Может, просто прерывание по CPA забыли разрешить?
нет, там всё норм.

Цитата
Как обрабатывается вектор прерывания в стартапе?
там стандартный IAR'овский обработчик:
Код
irqHandler:
        /* Save interrupt context on the stack to allow nesting */
        SUB     lr, lr, #4
        STMFD   sp!, {lr}
        MRS     lr, SPSR
        STMFD   sp!, {r0, lr}

        /* Write in the IVR to support Protect Mode */
        LDR     lr, =AT91C_BASE_AIC
        LDR     r0, [r14, #AIC_IVR]
        STR     lr, [r14, #AIC_IVR]

        /* Branch to interrupt handler in Supervisor mode */
        MSR     CPSR_c, #ARM_MODE_SYS
        STMFD   sp!, {r1-r3, r4, r12, lr}
        MOV     lr, pc
        BX      r0
        LDMIA   sp!, {r1-r3, r4, r12, lr}
        MSR     CPSR_c, #ARM_MODE_IRQ | I_BIT

        /* Acknowledge interrupt */
        LDR     lr, =AT91C_BASE_AIC
        STR     lr, [r14, #AIC_EOICR]

        /* Restore interrupt context and branch back to calling code */
        LDMIA   sp!, {r0, lr}
        MSR     SPSR_cxsf, lr
        LDMIA   sp!, {pc}^
aaarrr
Цитата(srm @ May 26 2010, 18:46) *
там стандартный IAR'овский обработчик:

Раз так, то никаких __irq не нужно. Как и записи EOICR в вашем обработчике.
srm
Цитата
Раз так, то никаких __irq не нужно. Как и записи EOICR в вашем обработчике.
запускаю на выполнение. ставлю бряк на обработчик прерывания: mask == 0x1001C, т.е. содержит все три флага: AT91C_TC_CPCS | AT91C_TC_CPAS | AT91C_TC_CPBS.

если же сначала поставлю бряк и поймаю самое первое прерывание, то всё нормально: mask == 0x10008, т.е. содержит только флаг AT91C_TC_CPBS.

magic
aaarrr
Проверьте, что в прерывании действительно читается SR (в дизассемблере). Убедитесь, что обработка прерывания не длится слишком долго.
Ну и уберите __irq и EOICR, если они еще остались.
srm
aaarrr, да, вроде, всё правильно.. может что-нибудь с режимом..

Код
m_timer->TC_CMR = calculate_optimal_divider(max_time_ms) | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_WAVE | AT91C_TC_EEVT_XC0;
aaarrr
Цитата(srm @ May 26 2010, 19:42) *
может что-нибудь с режимом.

Может. Выложите всю инициализацию, включая calculate_optimal_divider() и т.п., иначе можно только гадать.
srm
aaarrr, ок. спасибо за желание помочь.

CODE
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);
srm
в симуляторе Keil всё пучком 07.gif
максимально упростил код. кому не лень, пожалуйста, скомпилируйте у себя и посмотрите чему равен 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 миллисекунд! тем более, что при уменьшении времени (вплодь до десятых долей мс) выводится примерно то же самое.
srm
В реалтайме, вроде, всё норм. У меня 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));
    }
  }
aaarrr
Ну а get_time_us() что собой представляет?

Желание использовать RTT как источник информации о времени представляется, мягко говоря, несколько странным: таймер работает от RC-генератора, частота которого по определению может бултыхаться в широких пределах. Единственное разумное его применение - это поднимать процессор из idle по будильнику.
srm
aaarrr, я, вроде, по применению и использую. насколько я понял из даташита, real time timer служит как раз для получения информации о текущем времени. Тем более, что большая точность мне не нужна. Время мне нужно для того, чтобы движения робота были синхронны. Ну и плюс для работы ПИД регуляторов.
get_time_us всего лишь запрашивает показание счётчика и переводит тики в микросекунды.
По ходу дела, все эти глюки происходят из-за jtag'a. если запускать программу на выполнение без отладки, при отключенном Jtag'е, то глюки, вроде, не повторяются.. В общем, magic!
aaarrr
Цитата(srm @ Jun 1 2010, 23:14) *
aaarrr, я, вроде, по применению и использую. насколько я понял из даташита, real time timer служит как раз для получения информации о текущем времени. get_time_us всего лишь запрашивает показание счётчика и переводит тики в микросекунды.

RC Oscillator Frequency: Min. 22 Typ. 32 Max. 42 kHz
Т.е. плюс-минус лапоть. Устраивает такое время?

Для формирования точных интервалов существует Periodic Interval Timer.
srm
мдя... робот будет ходить либо вдвое быстрее, либо вдвое медленнее smile.gif)))))
aaarrr
Цитата(srm @ Jun 1 2010, 23:35) *
мдя... робот будет ходить либо вдвое быстрее, либо вдвое медленнее smile.gif)))))

Вот именно. Тут не 5%, а совсем неприлично.
Nikitoc
Добрый день. Дабы не плодить похожих тем решил спросить здесь.
Пытаюсь вызвать прерывание от 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. Однако, перехода по вектору не происходит...
aaarrr
Цитата(Nikitoc @ Jun 25 2010, 11:47) *
Код
AT91C_AIC_SRCTYPE_POSITIVE_EDGE

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

А уж прерывания RTT - это просто одно большое недоразумение. Для их использования нужно иметь ну очень веские основания.
Nikitoc
Цитата(aaarrr @ Jun 25 2010, 11:46) *
Ну почему, почему все упорно пытаются работать по фронту, а? Для внутренних источников это в 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.gif
aaarrr
Цитата(Nikitoc @ Jun 25 2010, 13:07) *
Изменил инициализацию таким образом:

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

Цитата(Nikitoc @ Jun 25 2010, 13:07) *
А обработчик так:

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

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

Ээээмммм.... А где они разрешаются? blush.gif
aaarrr
Разрешаются сбросом бита I регистра CPSR процессора. Из-под 'C' обычно предусматривается доступ в виде intrinsic-функций типа __enable_inerttupts(), или чего-то подобного - смотрите хелп своего компилятора.
Nikitoc
Цитата(aaarrr @ Jun 25 2010, 13:38) *
Разрешаются сбросом бита I регистра CPSR процессора. Из-под 'C' обычно предусматривается доступ в виде intrinsic-функций типа __enable_inerttupts(), или чего-то подобного - смотрите хелп своего компилятора.

Спасибо большое совсем забыл про эту особенность армов. Сделал в стартапе небольшую вставочку (в самом начале):
Код
                MRS        R0, CPSR        ; enable_irq
                BIC     R0,R0, #0x00000080; enable_irq
                MSR     CPSR_c, R0        ; enable_irq

И битик I в регистре CPSR сбросился rolleyes.gif . Но прерывания все равно не работают. Более того, после инициализации RTT, ожидая прерывания, я остановил RUN отладчика (ядро) и процессор находился в стартапе в RESET_HANDLER и выходить оттудова не собирался... Я вообще в ступор впал...

P.S. У меня подозрение закралось, что прерывание происходит на RESET_HANDLER. Но почему? Может где-то нужно переопределить вектора прерывания IRQ? Но я ведь в стартапе это сделал...
aaarrr
Цитата(Nikitoc @ Jun 25 2010, 15:22) *
Сделал в стартапе небольшую вставочку (в самом начале):

Логичнее было бы ее сделать в самом конце: так вы разрешили прерывания до всех настроек и сбросов периферии (что чревато), и только для режима SVC. На 'C' часть процессор может и в USER/SYSTEM уходить.


Цитата(Nikitoc @ Jun 25 2010, 15:22) *
P.S. У меня подозрение закралось, что прерывание происходит на RESET_HANDLER. Но почему? Может где-то нужно переопределить вектора прерывания IRQ? Но я ведь в стартапе это сделал...

Не умеет туда прерывание уходить по определению. "Топтаться" на RESET_HANDLER процессор тоже не умеет, если reset снят и есть клоки.
Nikitoc
Цитата(aaarrr @ Jun 25 2010, 14:41) *
Не умеет туда прерывание уходить по определению. "Топтаться" на RESET_HANDLER процессор тоже не умеет, если reset снят и есть клоки.

Я неправильно выразился. Я имел в виду, что процессор сбрасываетя по вектору Reset_Handler, нормально работает до конструкции:
Код
IMPORT  __main
                LDR     R0, =__main
                BX      R0

а потом опять сбрасывается и так по кругу.

Добавил через 10 мин:
А теперь запутался окончательно. Поставил разрешение прерываний перед входом в main и теперь процик все равно сбрасывается, и в прерывание не входит.
Немного непонятна сия конструкция из стартапа:
Код
Vectors         LDR     PC,Reset_Addr        
                LDR     PC,Undef_Addr
                LDR     PC,SWI_Addr
                LDR     PC,PAbt_Addr
                LDR     PC,DAbt_Addr
                NOP                         ; Reserved Vector
             ;LDR     PC,IRQ_Addr
                LDR     PC,[PC,#-0xF20]     ; Vector From AIC_IVR
             ;LDR     PC,FIQ_Addr
                LDR     PC,[PC,#-0xF20]     ; Vector From AIC_FVR

А именно то, что стоит вместо закомментированных строк.
aaarrr
Поздравляю, работают теперь прерывания. LDR PC,[PC,#-0xF20] - это переход по адресу, содержащемуся в AIC_IVR. Так как соответствующий вектор вы не прописали, то процессор переходит по адресу 0.
Добавьте строку:
Код
    AT91C_BASE_AIC->AIC_SVR[AT91C_ID_SYS] = (unsigned int)RTTC_Interrupt_Handler;

И будет счастье.

На будущее: выкладывайте сразу стартап целиком. Невозможно угадать, что в нем написано и закомментировано.
Пожалуй, надо FAQ написать про прерывания на ARM'ах разных моделей...
Nikitoc
Цитата(aaarrr @ Jun 25 2010, 15:31) *
Поздравляю, работают теперь прерывания. LDR PC,[PC,#-0xF20] - это переход по адресу, содержащемуся в AIC_IVR. Так как соответствующий вектор вы не прописали, то процессор переходит по адресу 0.
Добавьте строку:
Код
    AT91C_BASE_AIC->AIC_SVR[AT91C_ID_SYS] = (unsigned int)RTTC_Interrupt_Handler;

И будет счастье.

Мозг начинает разрываться на части. Ход Ваших мыслей мне ясен. Вместо строк
Код
LDR     PC,[PC,#-0xF20]
я раскомментировал
Код
LDR     PC,IRQ_Addr
и
Код
LDR     PC,FIQ_Addr
Вроде бы здесь все должно было заработать, однако, по каким-то необъяснимым причинам происходит FIQ прерывание через примерно секунду после старта программы (это при том что RTT запрограммирован на 5сек.). Запретить его нету никакой возможности (ни в стартапе через CPSR ни через AIC)! Крыша едет!
aaarrr
Цитата(Nikitoc @ Jun 25 2010, 17:23) *
я раскомментировал

Раскомментировали, похоже, неаккуратно. И зачем это надо было делать вообще?
Nikitoc
Цитата(aaarrr @ Jun 25 2010, 16:32) *
Раскомментировали, похоже, неаккуратно.

Вот:
Код
Vectors         LDR     PC,Reset_Addr        
                LDR     PC,Undef_Addr
                LDR     PC,SWI_Addr
                LDR     PC,PAbt_Addr
                LDR     PC,DAbt_Addr
                NOP                           ; Reserved Vector
                LDR     PC,IRQ_Addr
               ;LDR     PC,[PC,#-0xF20]       ; Vector From AIC_IVR
                LDR     PC,FIQ_Addr
               ;LDR     PC,[PC,#-0xF20]       ; Vector From AIC_FVR

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


Цитата
И зачем это надо было делать вообще?


Ну чтобы не записывать адрес в IVR и FVR. Кстати, а почему в стартапе для обоих векторов адрес берется по одному и тому же адресу -
Код
LDR     PC,[PC,#-0xF20]
?
aaarrr
Цитата(Nikitoc @ Jun 25 2010, 17:41) *
Ну чтобы не записывать адрес в IVR и FVR.

Так все равно потом придется.

Цитата(Nikitoc @ Jun 25 2010, 17:41) *
Кстати, а почему в стартапе для обоих векторов адрес берется по одному и тому же адресу -
Код
LDR     PC,[PC,#-0xF20]
?

Адреса команд разные, следовательно одно и то же значение смещения относительно PC даст разные адреса в памяти.
Nikitoc
Цитата(aaarrr @ Jun 25 2010, 16:53) *
Так все равно потом придется.

Сделал и так (раскомментировал строки
Код
LDR     PC,[PC,#-0xF20]
) и записал адреса в SVR[0] для FIQ и SVR[1] для SYSIRQ. И все равно происходит почему-то FIQ и программа уходит именно на адрес записанный в SVR[0]! (и режим работы процессора меняется на Fast Interrupt) Почему? Может надо сбрасывать флаги?
Цитата
Адреса команд разные, следовательно одно и то же значение смещения относительно PC даст разные адреса в памяти.

Угу. Спасибо. Понял.



Ээх. Придется отказаться от использования прерываний. Как об стену горохом все попытки...
Буду периодически опрашивать флаги, но это,конечно, изврат.
aaarrr
Цитата(Nikitoc @ Jun 25 2010, 18:57) *
Ээх. Придется отказаться от использования прерываний. Как об стену горохом все попытки...

Зачем же сдаваться? Проект выложите - посмотрим.

Цитата(Nikitoc @ Jun 25 2010, 18:57) *
Буду периодически опрашивать флаги, но это,конечно, изврат.

Для RTT это совсем не изврат. Единственное разумное применение его прерываний - периодически поднимать процессор из спячки, если нужно.
Nikitoc
Цитата
Для RTT это совсем не изврат. Единственное разумное применение его прерываний - периодически поднимать процессор из спячки, если нужно.

Да я для этого его и планировал использовать.
P.S. Не знаю правильно ли я выложил код?
Просьба не придираться к п/п PIO_INIT()
aaarrr
Цитата(Nikitoc @ Jun 25 2010, 19:15) *
P.S. Не знаю правильно ли я выложил код?

Нет, приложите в виде архива с проектом - зачем такие полотна плодить?
Nikitoc
Цитата(aaarrr @ Jun 25 2010, 18:48) *
Нет, приложите в виде архива с проектом - зачем такие полотна плодить?

Загрузил:
aaarrr
А зачем это вы упразднили модификаторы __irq? Они должны быть в вашем случае.
Nikitoc
Цитата(aaarrr @ Jun 25 2010, 19:02) *
А зачем это вы упразднили модификаторы __irq? Они должны быть в вашем случае.

Ну я так посмотрел - в регистры IVR и FVR заносятся те же значения, что с модификаторами _irq, что без них. Т.е. адреса перехода не меняются. И работает все так же. Почему то происходит именно быстрое прерывание, но не всегда, примерно в 90% случаев. А если не происходит быстрое то и обычное тоже не происходит.
aaarrr
Цитата(Nikitoc @ Jun 26 2010, 11:01) *
Ну я так посмотрел - в регистры IVR и FVR заносятся те же значения, что с модификаторами _irq, что без них. Т.е. адреса перехода не меняются. И работает все так же.

Нет, не так же. На входе в прерывание это действительно не скажется, зато на выходе - еще как.

Цитата(Nikitoc @ Jun 26 2010, 11:01) *
Почему то происходит именно быстрое прерывание, но не всегда, примерно в 90% случаев. А если не происходит быстрое то и обычное тоже не происходит.

Посмотрел код.

В стартапе IRQ и FIQ уже были включены:
Код
;  Enter User Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_USR


А вот и разгадка возникновения FIQ:
Код
   pAIC->AIC_IECR = AT91C_ID_SYS;

Должно быть 1 << AT91C_ID_SYS.

Ну и до кучи:
Код
AT91C_BASE_PIOA -> PIO_CODR = AT91C_BASE_PIOA -> PIO_CODR | 0x80000000;

CODR - это регистр только для записи, поэтому правильно так:
Код
AT91C_BASE_PIO->PIO_CODR = 0x80000000;

Nikitoc
Цитата
Нет, не так же. На входе в прерывание это действительно не скажется, зато на выходе - еще как.

Ага понял. Наверное этот модификатор добавляет в п/п команды сохранения и восстановления контекста?
Цитата
В стартапе IRQ и FIQ уже были включены:
Код
     ;Enter User Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_USR

Пожалуйста, объясните мне эту строчку. Не могу никак понять что за регистр такой CPSR_c (и как изменить его содержимое, чтобы запретить FIQ) и адрес #Mode_USR?

Цитата
А вот и разгадка возникновения FIQ:
Код
   pAIC->AIC_IECR = AT91C_ID_SYS;

Должно быть 1 << AT91C_ID_SYS.

Да (звучат фанфары)!!! Скорее всего это оно! Такая мелочь, аж обидно. Вместо IRQ разрешаю FIQ.

Цитата
Ну и до кучи:
Код
AT91C_BASE_PIOA -> PIO_CODR = AT91C_BASE_PIOA -> PIO_CODR | 0x80000000;

CODR - это регистр только для записи, поэтому правильно так:
Код
AT91C_BASE_PIO->PIO_CODR = 0x80000000;

Ага. На эту ошибку Вы мне уже когда-то указывали. Все равно где-то опять проскакивает.
В общем, огромное Вам спасибо, aaarrr, благодаря Вам пришлось достаточно глубоко вникать в прерывания rolleyes.gif ну и программка скорее всего теперь будет работать как планировалось. Правда раньше понедельника я это не смогу узнать.
aaarrr
Цитата(Nikitoc @ Jun 26 2010, 16:31) *
Ага понял. Наверное этот модификатор добавляет в п/п команды сохранения и восстановления контекста?

И модифицирует адрес возврата.

Цитата(Nikitoc @ Jun 26 2010, 16:31) *
Пожалуйста, объясните мне эту строчку. Не могу никак понять что за регистр такой CPSR_c (и как изменить его содержимое, чтобы запретить FIQ) и адрес #Mode_USR?

CPSR - регистр, _c - указатель модифицируемого поля (Control bits: I, F, T, M[4:0]). Mode_USR - это не адрес, а константа, задающая режим процессора.
Nikitoc
Цитата(aaarrr @ Jun 26 2010, 16:15) *
CPSR - регистр, _c - указатель модифицируемого поля (Control bits: I, F, T, M[4:0]). Mode_USR - это не адрес, а константа, задающая режим процессора.

Ну теперь вообще все ясно (так глядишь и ассемблер выучу smile.gif)
Кстати, Вы в этой теме раньше писали:
Цитата
Еще обратите внимание, что что флаги RTT сбрасываются через 2 периода SCLK (~61us) после чтения статусного регистра. Поэтому попасть в прерывание просто, а вот выйти из него - не очень sad.gif

Как это обрабатывать? Разве флаги прерывания не сбрасываются (при наличии модификатора _irq) при выходе из п/п обработки?
aaarrr
Цитата(Nikitoc @ Jun 26 2010, 21:02) *
Как это обрабатывать? Разве флаги прерывания не сбрасываются (при наличии модификатора _irq) при выходе из п/п обработки?

У ядра нет флагов прерывания, только входной сигнал. Если он в активном уровне и бит I регистра CPSR сброшен, процессор переходит на вектор IRQ. Сигнал формируется AIC'ом на основании состояния входных сигналов от периферии, масок источников и состояния логики приоритетов. Сбрасывать там что-то можно только если источник настроен на работу по фронту (тут еще можно написать несколько абзацев, чем чревата такая настройка). Но в любом случае необходимо также выполнить сброс источника в периферийном устройстве. В случае RTT, который тактируется от SCLK, он занимает несколько больше времени, чем хотелось бы. Если прерывание используется для побудки процессора (т.е. не слишком часто), можно просто подождать сброса флагов в самой процедуре прерывания, попутно, возможно, занявшись чем-нибудь полезным.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.