|
real time timer - прерывание по переполнению |
|
|
|
May 25 2010, 15:42
|
Частый гость
 
Группа: Участник
Сообщений: 144
Регистрация: 27-12-09
Из: Пермь
Пользователь №: 54 501

|
Не нашёл как установить обработчик прерывания по переполнению для таймера реального времени. Нужно ведь как-то так: Код AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, ???, m_interrupt_priority, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, (void(*)())interrupt_handler); А я не знаю какой у него ID..
|
|
|
|
|
 |
Ответов
(1 - 51)
|
May 26 2010, 13:45
|
Частый гость
 
Группа: Участник
Сообщений: 144
Регистрация: 27-12-09
Из: Пермь
Пользователь №: 54 501

|
Столкнулся с такой проблемой. Я поставил делитель 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.
Сообщение отредактировал srm - May 26 2010, 13:03
|
|
|
|
|
May 26 2010, 14:17
|
Частый гость
 
Группа: Участник
Сообщений: 144
Регистрация: 27-12-09
Из: Пермь
Пользователь №: 54 501

|
Локализовал проблему. Вот обработчик прерывания: Код 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 стек не переполнен...
Сообщение отредактировал srm - May 26 2010, 14:29
|
|
|
|
|
May 26 2010, 14:39
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(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 стек не переполнен... Как обрабатывается вектор прерывания в стартапе?
|
|
|
|
|
May 26 2010, 14:46
|
Частый гость
 
Группа: Участник
Сообщений: 144
Регистрация: 27-12-09
Из: Пермь
Пользователь №: 54 501

|
Цитата Может, просто прерывание по 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}^
Сообщение отредактировал srm - May 26 2010, 14:46
|
|
|
|
|
May 26 2010, 15:01
|
Частый гость
 
Группа: Участник
Сообщений: 144
Регистрация: 27-12-09
Из: Пермь
Пользователь №: 54 501

|
Цитата Раз так, то никаких __irq не нужно. Как и записи EOICR в вашем обработчике. запускаю на выполнение. ставлю бряк на обработчик прерывания: mask == 0x1001C, т.е. содержит все три флага: AT91C_TC_CPCS | AT91C_TC_CPAS | AT91C_TC_CPBS. если же сначала поставлю бряк и поймаю самое первое прерывание, то всё нормально: mask == 0x10008, т.е. содержит только флаг AT91C_TC_CPBS. magic
|
|
|
|
|
May 26 2010, 15:42
|
Частый гость
 
Группа: Участник
Сообщений: 144
Регистрация: 27-12-09
Из: Пермь
Пользователь №: 54 501

|
aaarrr, да, вроде, всё правильно.. может что-нибудь с режимом.. Код m_timer->TC_CMR = calculate_optimal_divider(max_time_ms) | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_WAVE | AT91C_TC_EEVT_XC0;
Сообщение отредактировал srm - May 26 2010, 15:42
|
|
|
|
|
May 26 2010, 16:10
|
Частый гость
 
Группа: Участник
Сообщений: 144
Регистрация: 27-12-09
Из: Пермь
Пользователь №: 54 501

|
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);
Сообщение отредактировал aaarrr - May 26 2010, 16:16
Причина редактирования: Оформление цитаты исходника
|
|
|
|
|
May 26 2010, 19:16
|
Частый гость
 
Группа: Участник
Сообщений: 144
Регистрация: 27-12-09
Из: Пермь
Пользователь №: 54 501

|
в симуляторе Keil всё пучком максимально упростил код. кому не лень, пожалуйста, скомпилируйте у себя и посмотрите чему равен 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 - May 26 2010, 19:23
|
|
|
|
|
May 27 2010, 16:35
|
Частый гость
 
Группа: Участник
Сообщений: 144
Регистрация: 27-12-09
Из: Пермь
Пользователь №: 54 501

|
Вроде, немного разобрался. Если поставить значение делителя 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 миллисекунд! тем более, что при уменьшении времени (вплодь до десятых долей мс) выводится примерно то же самое.
|
|
|
|
|
Jun 1 2010, 18:14
|
Частый гость
 
Группа: Участник
Сообщений: 144
Регистрация: 27-12-09
Из: Пермь
Пользователь №: 54 501

|
В реалтайме, вроде, всё норм. У меня 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 - Jun 1 2010, 18:19
|
|
|
|
|
Jun 1 2010, 19:14
|
Частый гость
 
Группа: Участник
Сообщений: 144
Регистрация: 27-12-09
Из: Пермь
Пользователь №: 54 501

|
aaarrr, я, вроде, по применению и использую. насколько я понял из даташита, real time timer служит как раз для получения информации о текущем времени. Тем более, что большая точность мне не нужна. Время мне нужно для того, чтобы движения робота были синхронны. Ну и плюс для работы ПИД регуляторов. get_time_us всего лишь запрашивает показание счётчика и переводит тики в микросекунды. По ходу дела, все эти глюки происходят из-за jtag'a. если запускать программу на выполнение без отладки, при отключенном Jtag'е, то глюки, вроде, не повторяются.. В общем, magic!
Сообщение отредактировал srm - Jun 1 2010, 19:18
|
|
|
|
|
Jun 25 2010, 07:47
|

Местный
  
Группа: Validating
Сообщений: 207
Регистрация: 14-01-09
Из: Днепропетровск
Пользователь №: 43 367

|
Добрый день. Дабы не плодить похожих тем решил спросить здесь. Пытаюсь вызвать прерывание от 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 - Jun 25 2010, 08:29
|
|
|
|
|
Jun 25 2010, 08:46
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(Nikitoc @ Jun 25 2010, 11:47)  Код AT91C_AIC_SRCTYPE_POSITIVE_EDGE Ну почему, почему все упорно пытаются работать по фронту, а? Для внутренних источников это в 99.9% случаев не нужно, для внешних - в 90%. В остальных 0.1% и 10% нужно проявлять предельную внимательность при обслуживании такого прерывания. В вашем случае отсутствует сброс источника в AIC, и запись в EOICR (хотя если принять во внимание переделку в стартапе, последнее уже несущественно). А уж прерывания RTT - это просто одно большое недоразумение. Для их использования нужно иметь ну очень веские основания.
|
|
|
|
|
Jun 25 2010, 09:07
|

Местный
  
Группа: Validating
Сообщений: 207
Регистрация: 14-01-09
Из: Днепропетровск
Пользователь №: 43 367

|
Цитата(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} Все равно не заходит!
Сообщение отредактировал Nikitoc - Jun 25 2010, 09:17
|
|
|
|
|
Jun 25 2010, 09:20
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(Nikitoc @ Jun 25 2010, 13:07)  Изменил инициализацию таким образом: ICCR работает только для прерываний по фронту, писать его здесь бессмысленно. Цитата(Nikitoc @ Jun 25 2010, 13:07)  А обработчик так: EOICR нужно писать в любом случае, если уж оказались в прерывании, а не по каким-либо еще условиям. Глобально прерывания разрешены? Отладчик малину не портит, часом?
|
|
|
|
|
Jun 25 2010, 11:22
|

Местный
  
Группа: Validating
Сообщений: 207
Регистрация: 14-01-09
Из: Днепропетровск
Пользователь №: 43 367

|
Цитата(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 сбросился  . Но прерывания все равно не работают. Более того, после инициализации RTT, ожидая прерывания, я остановил RUN отладчика (ядро) и процессор находился в стартапе в RESET_HANDLER и выходить оттудова не собирался... Я вообще в ступор впал... P.S. У меня подозрение закралось, что прерывание происходит на RESET_HANDLER. Но почему? Может где-то нужно переопределить вектора прерывания IRQ? Но я ведь в стартапе это сделал...
Сообщение отредактировал Nikitoc - Jun 25 2010, 11:29
|
|
|
|
|
Jun 25 2010, 11:41
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(Nikitoc @ Jun 25 2010, 15:22)  Сделал в стартапе небольшую вставочку (в самом начале): Логичнее было бы ее сделать в самом конце: так вы разрешили прерывания до всех настроек и сбросов периферии (что чревато), и только для режима SVC. На 'C' часть процессор может и в USER/SYSTEM уходить. Цитата(Nikitoc @ Jun 25 2010, 15:22)  P.S. У меня подозрение закралось, что прерывание происходит на RESET_HANDLER. Но почему? Может где-то нужно переопределить вектора прерывания IRQ? Но я ведь в стартапе это сделал... Не умеет туда прерывание уходить по определению. "Топтаться" на RESET_HANDLER процессор тоже не умеет, если reset снят и есть клоки.
|
|
|
|
|
Jun 25 2010, 12:05
|

Местный
  
Группа: Validating
Сообщений: 207
Регистрация: 14-01-09
Из: Днепропетровск
Пользователь №: 43 367

|
Цитата(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 А именно то, что стоит вместо закомментированных строк.
Сообщение отредактировал Nikitoc - Jun 25 2010, 12:24
|
|
|
|
|
Jun 25 2010, 12:31
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

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

Местный
  
Группа: Validating
Сообщений: 207
Регистрация: 14-01-09
Из: Днепропетровск
Пользователь №: 43 367

|
Цитата(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)! Крыша едет!
Сообщение отредактировал Nikitoc - Jun 25 2010, 13:25
|
|
|
|
|
Jun 25 2010, 13:41
|

Местный
  
Группа: Validating
Сообщений: 207
Регистрация: 14-01-09
Из: Днепропетровск
Пользователь №: 43 367

|
Цитата(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] ?
|
|
|
|
|
Jun 25 2010, 13:53
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

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

Местный
  
Группа: Validating
Сообщений: 207
Регистрация: 14-01-09
Из: Днепропетровск
Пользователь №: 43 367

|
Цитата(aaarrr @ Jun 25 2010, 16:53)  Так все равно потом придется. Сделал и так (раскомментировал строки Код LDR PC,[PC,#-0xF20] ) и записал адреса в SVR[0] для FIQ и SVR[1] для SYSIRQ. И все равно происходит почему-то FIQ и программа уходит именно на адрес записанный в SVR[0]! (и режим работы процессора меняется на Fast Interrupt) Почему? Может надо сбрасывать флаги? Цитата Адреса команд разные, следовательно одно и то же значение смещения относительно PC даст разные адреса в памяти. Угу. Спасибо. Понял. Ээх. Придется отказаться от использования прерываний. Как об стену горохом все попытки... Буду периодически опрашивать флаги, но это,конечно, изврат.
Сообщение отредактировал Nikitoc - Jun 25 2010, 14:57
|
|
|
|
|
Jun 25 2010, 15:01
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(Nikitoc @ Jun 25 2010, 18:57)  Ээх. Придется отказаться от использования прерываний. Как об стену горохом все попытки... Зачем же сдаваться? Проект выложите - посмотрим. Цитата(Nikitoc @ Jun 25 2010, 18:57)  Буду периодически опрашивать флаги, но это,конечно, изврат. Для RTT это совсем не изврат. Единственное разумное применение его прерываний - периодически поднимать процессор из спячки, если нужно.
|
|
|
|
|
Jun 25 2010, 15:15
|

Местный
  
Группа: Validating
Сообщений: 207
Регистрация: 14-01-09
Из: Днепропетровск
Пользователь №: 43 367

|
Цитата Для RTT это совсем не изврат. Единственное разумное применение его прерываний - периодически поднимать процессор из спячки, если нужно. Да я для этого его и планировал использовать. P.S. Не знаю правильно ли я выложил код? Просьба не придираться к п/п PIO_INIT()
Сообщение отредактировал aaarrr - Jun 25 2010, 15:47
|
|
|
|
|
Jun 25 2010, 16:00
|

Местный
  
Группа: Validating
Сообщений: 207
Регистрация: 14-01-09
Из: Днепропетровск
Пользователь №: 43 367

|
Цитата(aaarrr @ Jun 25 2010, 18:48)  Нет, приложите в виде архива с проектом - зачем такие полотна плодить? Загрузил:
|
|
|
|
|
Jun 26 2010, 08:41
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(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;
|
|
|
|
|
Jun 26 2010, 12:31
|

Местный
  
Группа: Validating
Сообщений: 207
Регистрация: 14-01-09
Из: Днепропетровск
Пользователь №: 43 367

|
Цитата Нет, не так же. На входе в прерывание это действительно не скажется, зато на выходе - еще как. Ага понял. Наверное этот модификатор добавляет в п/п команды сохранения и восстановления контекста? Цитата В стартапе 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, благодаря Вам пришлось достаточно глубоко вникать в прерывания  ну и программка скорее всего теперь будет работать как планировалось. Правда раньше понедельника я это не смогу узнать.
|
|
|
|
|
Jun 26 2010, 13:15
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(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 - это не адрес, а константа, задающая режим процессора.
|
|
|
|
|
Jun 26 2010, 17:02
|

Местный
  
Группа: Validating
Сообщений: 207
Регистрация: 14-01-09
Из: Днепропетровск
Пользователь №: 43 367

|
Цитата(aaarrr @ Jun 26 2010, 16:15)  CPSR - регистр, _c - указатель модифицируемого поля (Control bits: I, F, T, M[4:0]). Mode_USR - это не адрес, а константа, задающая режим процессора. Ну теперь вообще все ясно (так глядишь и ассемблер выучу  ) Кстати, Вы в этой теме раньше писали: Цитата Еще обратите внимание, что что флаги RTT сбрасываются через 2 периода SCLK (~61us) после чтения статусного регистра. Поэтому попасть в прерывание просто, а вот выйти из него - не очень sad.gif Как это обрабатывать? Разве флаги прерывания не сбрасываются (при наличии модификатора _irq) при выходе из п/п обработки?
|
|
|
|
|
Jun 26 2010, 17:16
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(Nikitoc @ Jun 26 2010, 21:02)  Как это обрабатывать? Разве флаги прерывания не сбрасываются (при наличии модификатора _irq) при выходе из п/п обработки? У ядра нет флагов прерывания, только входной сигнал. Если он в активном уровне и бит I регистра CPSR сброшен, процессор переходит на вектор IRQ. Сигнал формируется AIC'ом на основании состояния входных сигналов от периферии, масок источников и состояния логики приоритетов. Сбрасывать там что-то можно только если источник настроен на работу по фронту (тут еще можно написать несколько абзацев, чем чревата такая настройка). Но в любом случае необходимо также выполнить сброс источника в периферийном устройстве. В случае RTT, который тактируется от SCLK, он занимает несколько больше времени, чем хотелось бы. Если прерывание используется для побудки процессора (т.е. не слишком часто), можно просто подождать сброса флагов в самой процедуре прерывания, попутно, возможно, занявшись чем-нибудь полезным.
|
|
|
|
|
Aug 1 2010, 08:07
|
Частый гость
 
Группа: Участник
Сообщений: 144
Регистрация: 27-12-09
Из: Пермь
Пользователь №: 54 501

|
В общем, глюк так и не удалось исправить. Периодически повторяется, если движения робота тактировать от RTT. Вот мой робот. В моменты времени 2:06, 2:16, 2:34 видны глюки (лапы уходят в стороны).
Сообщение отредактировал srm - Aug 1 2010, 08:08
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|