Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Обработчик прерывания срабатывает сразу после запуска таймера
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
MaxiMuz
Запрограммировал таймер на отчет временных интервалов:
Код
/* Настройка таймера TIM2 на событие: Прерывание при совпадении с р-ром TIM2_ARR */
    TIM2->ARR=(8000); // загрузка рег-ра для сравнения 366/8000=0,046 Hz
    TIM2->PSC=(65536-1);        // предделитель CK_CNT=24000000/65536=366 Hz
    TIM2->DIER|=(TIM_DIER_UIE); // разрешаем прерывание по срабатыванию таймера
    TIM2->CNT=1;
    TIM2->CR1|=(TIM_CR1_CEN); // Запуск Таймера

/* Разрешение прерывания TIM2 */
    NVIC_EnableIRQ(TIM2_IRQn);

Период србатывания ~21сек.
В обработчике поставил маркер срабатывания:
Код
void TIM2_IRQHandler (void)
{
    u32 i;
    TIM2->SR &=~(TIM_SR_UIF);// сброс ф.прерывания
    SWITCH_PIN(PC8);
}

При запуске программы почемуто сразу срабатывает прерывание. С какого это оно срабатывает , если событие не наступило?
Как сделать чтобы первое срабатывание было только по совпадению с рег-ом ARR ?
KnightIgor
Цитата(MaxiMuz @ Apr 18 2014, 14:11) *
При запуске программы почемуто сразу срабатывает прерывание. С какого это оно срабатывает , если событие не наступило?
Как сделать чтобы первое срабатывание было только по совпадению с рег-ом ARR ?

Сначала вопрос, не под отладчиком ли Вы пробуете: если не предпринять мер, а именно запретить счет таймера во время остановки при отладки, то таймер будет бежать дальше.
Регистры ARR и PSC имеют теневые регистры. Команды записи модифицируют теневые регистры, которые перепишутся в рабочие, когда установится флаг обновления:
Цитата
It can be changed on the fly as this control register is buffered. The new prescaler ratio is taken into account at the next update event.

Это означает, что новые значения регистров, которые Вы записали при инициализации, не действуют, пока не наступит событие переполнения, а оно наступает быстро, т.к. по умолчанию PSC равен нулю.
Действия:
- записать, что надо, прерывание еще не разрешать,
- установить искусственно флаг UG в регистре EGR - это обновит PSC|ARR новыми значениями.
- сбросить установившиеся флаги прерываний
- только теперь разрешить прерывания и запустить таймер.
Golikov A.
Цитата
TIM2->SR &=~(TIM_SR_UIF);


и это стоит проверить, в том ли регистре идет сброс, и так ли он идет? Обычно делают сбросы через = а не &=(|=), по ряду причин... но это надо проверить. Если регистр не чувствителен к записи 1 или 0 - это явный намек на это
KnightIgor
Цитата(Golikov A. @ Apr 19 2014, 15:31) *
и это стоит проверить, в том ли регистре идет сброс, и так ли он идет?

В описании регистра для бита UIF стоит /w0, что означает, что флаг сбрасывается при записи нуля. Так что все правильно. Конечно, можно замутить что-то элегантно и с bitband. Это в EFM32 есть "регистр сброса битов статуса", куда надо писать единицу для сброса бита регистра статуса в ноль. В STM32 такой подход применен только в GPIO.
Golikov A.
я к тому что надо писать не
TIM2->SR &= ~(TIM_SR_UIF);

а

TIM2->SR = ~(TIM_SR_UIF);

я не прав? Остальные биты регистра читаемые? Реагируют ли они на 1?

почему = лучше чем &= уже много раз обсуждали....
aaarrr
Цитата(Golikov A. @ Apr 20 2014, 01:18) *
почему = лучше чем &= уже много раз обсуждали....

Оно не лучше, оно для разных случаев.
"&=" или "|=" вместо "=" - типичная ошибка при работе с write-only регистрами.
Но как раз в случае STM так делать нельзя, по причине наличия такого примечания
для незадействованных битов в регистрах: Reserved, must be kept at reset value.
Так что только "&=" и "|=".
AHTOXA
Цитата(MaxiMuz @ Apr 18 2014, 19:11) *
При запуске программы почемуто сразу срабатывает прерывание. С какого это оно срабатывает , если событие не наступило?


Регистр PSC обновляется только при update event.
Если ARPE = 1, то ARR тоже обновляется только при update event (иначе сразу).

Добавьте перед запуском таймера:
Код
    // generate an update event to load the PSC and ARR values immediately
    TIM3->EGR = TIM_EGR_UG;
Golikov A.
Цитата(aaarrr @ Apr 20 2014, 01:42) *
Оно не лучше, оно для разных случаев.
"&=" или "|=" вместо "=" - типичная ошибка при работе с write-only регистрами.
Но как раз в случае STM так делать нельзя, по причине наличия такого примечания
для незадействованных битов в регистрах: Reserved, must be kept at reset value.
Так что только "&=" и "|=".


нет оно именно лучше.

если в регистре несколько битов - флагов, то
&= |=
срабатывая в 3 такта
забрать значение, изменить, записать обратно
может затереть возникший за эти три такта новый флаг, не говоря уже о чтение битов, которые предназначены только для записи.

Поэтому если в регистре есть биты которые должны оставаться всегда в ресет значениях, значит это должно быть учтено при
REGISTR = SBROS, в величине SBROS, но не как не означает что надо делать &=, |=....

REGISTR = (DEFAULT_VAL & (~INTERRUPT)); - правильно
REGISTR &= (~INTERRUPT) ==== REGISTR = REGISTR & (~INTERRUPT); - не правильно!
aaarrr
Цитата(Golikov A. @ Apr 20 2014, 09:53) *
может затереть возникший за эти три такта новый флаг,

Если битов несколько, и есть такая вероятность, то да, может.

Цитата(Golikov A. @ Apr 20 2014, 09:53) *
не говоря уже о чтение битов, которые предназначены только для записи.

Тут уж что-то одно - или write-only, или w0. Но никак не одновременно.

Для обсуждаемого случая "&=" является вполне корректной конструкцией.
adnega
Цитата(aaarrr @ Apr 20 2014, 13:33) *
Для обсуждаемого случая "&=" является вполне корректной конструкцией.

Вряд ли. Если в момент "чтение-модификация-запись" произойдет установка какого-нибудь другого флага, то он будет сброшен без обработки.
Golikov A.
Цитата(adnega @ Apr 20 2014, 19:05) *
Вряд ли. Если в момент "чтение-модификация-запись" произойдет установка какого-нибудь другого флага, то он будет сброшен без обработки.


как я понял автор настаивает на том, что в этом регистре всего один флаг (или используется только он), и потому ничего нельзя затереть. Другое дело что потом понадобиться другой флаг, или будет перенос кода обработчика через ctrl-c, ctrl-v или переход на другой проц с несколькими флагами, но кто у нас об этом думает...
aaarrr
Цитата(Golikov A. @ Apr 20 2014, 21:06) *
Другое дело что потом понадобиться другой флаг, или будет перенос кода обработчика через ctrl-c, ctrl-v или переход на другой проц с несколькими флагами, но кто у нас об этом думает...

Да мало ли способов накосячить? Это лишь один из многих.

P.S. Специально раскопал свой (целый один) проект с STM32. Таки да, прерывания в нем сбрасываю через = FLAG. Сброс более одного флага в обработчике используется только в модуле USB.
MaxiMuz
Цитата(KnightIgor @ Apr 18 2014, 18:18) *
Сначала вопрос, не под отладчиком ли Вы пробуете: если не предпринять мер, а именно запретить счет таймера во время остановки при отладки, то таймер будет бежать дальше.

Нет , отлаживаю все в железе по "лампочкам"
MaxiMuz
Цитата(aaarrr @ Apr 20 2014, 12:33) *
Для обсуждаемого случая "&=" является вполне корректной конструкцией.

Да , действительно , если в свойствах битов регистра "rc_w0" -означает очистка 0 , то корректнее очищать флаг:
Код
REG= ~(BIT_MASK);

Но если используется всего один флаг , то так
Код
TIM2->SR &=~(TIM_SR_UIF);

тоже правильно
AHTOXA
Цитата(MaxiMuz @ Apr 21 2014, 18:31) *

Я надеюсь, что среди всех этих полезных рассуждений о способах сброса флагов вы не пропустили сообщение номер 7, в котором я объясняю причины вашей исходной проблемы и даю способ её решения? sm.gif
MaxiMuz
Цитата(AHTOXA @ Apr 20 2014, 08:24) *
Добавьте перед запуском таймера:
Код
    // generate an update event to load the PSC and ARR values immediately
     TIM3->EGR = TIM_EGR_UG;

пробывал вот так:
Код
/* Настройка таймера TIM2 на событие: Прерывание при совпадении с р-ром TIM2_ARR */
    TIM2->ARR=(6500); // загрузка рег-ра для сравнения 366,2109375/350=1 Hz
    TIM2->PSC=(65535-1);        // предделитель CK_CNT=24000000/65536=366,2109375 Hz
  
    TIM2->DIER|=(TIM_DIER_UIE); // разрешаем прерывание по срабатыванию таймера    
    TIM2->EGR = TIM_EGR_UG;
    TIM2->SR =~(TIM_SR_UIF);// сброс ф.прерывания
    TIM2->CR1|=(TIM_CR1_CEN); // Запуск Таймера

/* Разрешение прерывания TIM2 */
    NVIC_EnableIRQ(TIM2_IRQn);

Не работает, таймер также запускается при ресет
AHTOXA
У вас прерывание назначено на событие обновления (UPDATE). Вы сначала разрешаете это прерывание, а потом генерируете событие обновления. Вот прерывание и возникает.
Надо сгенерировать событие обновления, потом сбросить флаг, а уже потом разрешать прерывания:
Код
    TIM2->ARR = 6500;
    TIM2->PSC = 65535-1;
    TIM2->EGR  = TIM_EGR_UG;
    TIM2->SR = ~TIM_SR_UIF;
    TIM2->DIER |= TIM_DIER_UIE;
    TIM2->CR1 |= TIM_CR1_CEN;
MaxiMuz
Цитата(AHTOXA @ Apr 22 2014, 11:32) *
Надо сгенерировать событие обновления, потом сбросить флаг, а уже потом разрешать прерывания:
Код
    TIM2->ARR = 6500;
     TIM2->PSC = 65535-1;
     TIM2->EGR  = TIM_EGR_UG;
     TIM2->SR = ~TIM_SR_UIF;
     TIM2->DIER |= TIM_DIER_UIE;
     TIM2->CR1 |= TIM_CR1_CEN;

тоже самое
Похоже что
TIM2->SR =~(TIM_SR_UIF);
не сбразывает сам "запрос" на прерывание
KnightIgor
Цитата(MaxiMuz @ Apr 22 2014, 10:17) *
тоже самое
Похоже что
TIM2->SR =~(TIM_SR_UIF);
не сбразывает сам "запрос" на прерывание

Попробуйте еще добавить:
CODE

TIM2->ARR = 6500;
TIM2->PSC = 65535-1;
TIM2->EGR = TIM_EGR_UG;
TIM2->SR = ~TIM_SR_UIF;
NVIC_ClearPendingIRQ(TIM2_IRQn);
TIM2->DIER |= TIM_DIER_UIE;
TIM2->CR1 |= TIM_CR1_CEN;
MaxiMuz
Выясненно опытным путем с использованием документа "Cortex-M3 programming manual".
Сброс флага статусного регистра
Код
    TIM2->SR =~(TIM_SR_UIF);
не приводит к сбросу самого запроса на прерывание.
Чтобы сбросить запрос на прерывания делаем:
1. После загрузки временных прарметров разрешаем прерывание по срабатыванию таймера
2. генерим событие таймера UG
3. Ждем один такт и только потом сбрасиываем флаг прерывания в регистре статуса таймера
4. Запускаем Таймер
5. Очищаем Interrupt clear-pending registers (NVIC_ICPRx)
6. И только потом включаем разрешение на прерывание
Код
/* Настройка таймера TIM2 на событие: Прерывание при совпадении с р-ром TIM2_ARR */
TIM2->ARR=(1500); // загрузка рег-ра для сравнения ~ 366/1500=0,244 Hz
    TIM2->PSC=(65535-1);        // предделитель CK_CNT=24000000/65536=366,2109375 Hz

TIM2->DIER|=(TIM_DIER_UIE); // разрешаем прерывание по срабатыванию таймера
TIM2->EGR = TIM_EGR_UG; // генерим событие таймера
__NOP();
TIM2->SR =~(TIM_SR_UIF);// сброс ф.прерывания
TIM2->CR1|=(TIM_CR1_CEN); // Запуск Таймера

NVIC_ClearPendingIRQ (TIM2_IRQn);

/* Разрешение прерывания TIM2 */
NVIC_EnableIRQ(TIM2_IRQn);

Если закоментить nop, то сброс события таймера не проходит.
Собственно вопрос такой , если мы в конце сбрасываем бит запроса на прерывания от таймера2 , тогда уже не важно какие события произошли до этого , прерывание по идее не ожидается, правильно ? Но если я убираю команду TIM2->EGR = TIM_EGR_UG; // генерим событие таймера , то таймер сразу после разрешения прерывания запускается.
Haamu
Цитата(MaxiMuz @ Apr 22 2014, 14:48) *
1. После загрузки временных прарметров разрешаем прерывание по срабатыванию таймера
2. генерим событие таймера UG
3. Ждем один такт и только потом сбрасиываем флаг прерывания в регистре статуса таймера
4. Запускаем Таймер
5. Очищаем Interrupt clear-pending registers (NVIC_ICPRx)
6. И только потом включаем разрешение на прерывание

Как-то сложно всё это получается. Попробуйте просто:
1. Запустить таймер
2. Разрешить прерывание
Именно в такой последовательности.
Golikov A.
Цитата
Но если я убираю команду TIM2->EGR = TIM_EGR_UG;

смысл команды не сгенерить прерывание
смысл команды вызвать событие и обновить регистры таймера, обновление которых происходит по событию, которое в свою очередь и генерит прерывание.
Прерывание в данном случае - побочный эффект от обновления таймера.

Цитата
Как-то сложно всё это получается. Попробуйте просто:
1. Запустить таймер
2. Разрешить прерывание
Именно в такой последовательности.


и будет исходная задача, без вызова события не произойдет настройки таймера,
а событие вызовет прерывание, потому сразу после разрешения прерывания в вашем случае полетите либо по таймеру работающему по старыми настройкам, либо если обновили настройки, то сразу по прерыванию...
HHIMERA
Цитата(Golikov A. @ Apr 20 2014, 08:53) *
REGISTR = (DEFAULT_VAL & (~INTERRUPT)); - правильно

Да тоже неправильно... sm.gif
DEFAULT_VAL равно нулю...
Golikov A.
правильно
DEFAULT_VAL равно не нулю, а тому чему оно должно быть равно для сохранения верных значений записываемых битиков.... это на случай если в регистры какие то битики обязаны записываться нулевыми, какие то единичками, правда я чет не припомню проца где это было нужно (в смысле где часть битов надо было оставлять 0, а часть 1, обычно все одинаковые)
HHIMERA
Цитата(Golikov A. @ Apr 23 2014, 09:23) *
DEFAULT_VAL равно не нулю, а тому чему оно должно быть равно для сохранения верных значений записываемых битиков....

Тогда уже лучше написать DEFAULT_VAL_MASK, где все биты с атрибутом RES должны быть нулями, а устанавливаемые флаги единицами... а не DEFAULT_VAL...
Я не ради докопаться... просто на другом форуме вопрошающий уже выдал непонимание происходящего...
aaarrr
Цитата(HHIMERA @ Apr 23 2014, 11:38) *
Тогда уже лучше написать DEFAULT_VAL_MASK, где все биты с атрибутом RES должны быть нулями, а устанавливаемые флаги единицами...

Их нельзя нулями. По мануалу нужно сохранять состояние после сброса. То есть "честное" формирование DEFAULT_VAL должно быть таким:
reg_def_val = reg | <все флаги типа w0c>
или
reg_def_val = reg & ~<все флаги типа w1c>

Не уверен, что кто-то так делает, конечно sm.gif

Цитата(Golikov A. @ Apr 23 2014, 10:23) *
я чет не припомню проца где это было нужно (в смысле где часть битов надо было оставлять 0, а часть 1, обычно все одинаковые)

Бывает, хотя в статусных регистрах тоже не встречал.
ViKo
Цитата(aaarrr @ Apr 23 2014, 10:49) *
Их нельзя нулями. По мануалу нужно сохранять состояние после сброса.

В данном случае Reset value: 0x0000, согласно мануалу.
Если речь идет о TIMx_SR.
HHIMERA
Цитата(aaarrr @ Apr 23 2014, 10:49) *
Не уверен, что кто-то так делает, конечно sm.gif

Да... сомнения присутствуют...
А по поводу...
Цитата
По мануалу нужно сохранять состояние после сброса.

Примеры SPL это отвергают напрочь... т.е. всё на совести и знаниях кодера...
Golikov A.
да я тоже часто в ПЛИС делают некоторые резервные биты, которые не использую, и сам их нулю внутри автоматов, но на всякий случай, мало ли чего забуду, в описании требую такие биты в регистрах не трогать. Думаю тут также, если их трогать скорее всего ничего не будет, но мало ли что, потому и приписка такая, чтоб наверняка!
HHIMERA
Цитата
если их трогать скорее всего ничего не будет

В каком-то таймере STM32... точно уже не помню... где по референсу бит OPM отсутствует... попробовал как-то его установить... на авось... а вдруг...
Результат - таймер затыкался... т.е. все эти предупреждения... как бы и не на ровном месте...
MaxiMuz
Задача своевременного запуска обработчика таймера решена.
Но хочу разобраться как связан бит UG c формированием запроса не прерывание .
Согласно описанию :
Цитата
Prescaler description
The prescaler can divide the counter clock frequency by any factor between 1 and 65536. It
is based on a 16-bit counter controlled through a 16-bit register (in the TIMx_PSC register).
It can be changed on the fly as this control register is buffered. The new prescaler ratio is
taken into account at the next update event.

Т.е. чтобы вызвать появление Update event (UEV) нужно чтобы счетчик досчитал до ARR. Поэтому я перед тем как Разрешить прерывание запускаю счетчик и естественным путем вызываю Update event (UEV) , а потом сбрасываю запрос на прерывание :
Код
    TIM2->ARR=(1);
    TIM2->PSC=(65535-1);
      
    TIM2->DIER|=(TIM_DIER_UIE); // разрешаем прерывание по срабатыванию таймера
    TIM2->CR1|=(TIM_CR1_CEN); // Запуск Таймера
    //TIM2->EGR = TIM_EGR_UG; // генерим событие таймера
    __NOP();
    __NOP();
    __NOP();
    TIM2->SR =~(TIM_SR_UIF);// сброс ф.прерывания
    __NOP();
    NVIC_ClearPendingIRQ (TIM2_IRQn);

/* Разрешение прерывания TIM2 */
    NVIC_EnableIRQ(TIM2_IRQn);

Почему тогда в этом случае , не происходит сброс запроса на прерывание ?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.