Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: stm32F429 и прерывание от таймера
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
zorromen
Здравствуйте, только начал осваивать проц, и столкнулся с непоняткой, уже 3 дня сижу и не могу понять.
Есть Выход
Код
   //Trigger
  /*Configure GPIO pin : PE3 */
  GPIO_InitStruct.Pin = GPIO_PIN_3;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_MEDIUM;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

Есть таймер 6, его настройка.
Код
    __TIM6_CLK_ENABLE();
    UV_state = 0;
        HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 1, 0);
        HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn);
    
    HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
    TIM6->PSC = 143;
    TIM6->ARR = 62499; //100 mS
    //TIM6->EGR = TIM_EGR_UG;
    TIM6->DIER = TIM_DIER_UIE;
    TIM6->CR1 |= TIM_CR1_CEN | TIM_CR1_ARPE;

Это прерывание
CODE
void TIM6_DAC_IRQHandler(void)
{
TIM6->CR1 &= ~TIM_CR1_CEN; //Остнавливаем таймер
TIM6->SR &= ~TIM_SR_UIF; //Сбрасываем флаг UIF

switch (UV_state)
{
case 0:
TIM6->PSC = 0;
TIM6->ARR = 0x707;//1799; //20 uS
TIM6->CNT = 0;
TIM6->CR1 |= TIM_CR1_CEN | TIM_CR1_ARPE;
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_SET);
UV_state = 1;
break;

case 1:
TIM6->PSC = 0x008f;//143
TIM6->ARR = 0xF423;//62499; //Ñðàáîòàòü íà 100 mS
TIM6->CNT = 0;
TIM6->CR1 |= TIM_CR1_CEN | TIM_CR1_ARPE;
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
UV_state = 0;
break;
}

}

Хочу иметь импульс 20 us через 100 ms, и вроде все логично, но на выходе все перевернуто
Нажмите для просмотра прикрепленного файла

Ну где тут можно ошибиться?
Заходим по состоянию 0, устанавливаем таймер на 20 us и устанавливаем ногу в 1, чтобы в следующем прерывании ее сбросить и настроить прерывание на 100 ms и потом ее установить в 1 в новом прерывании. Почему на выходе все наоборот?
SSerge
Наводящий вопрос: за что отвечает бит TIM_CR1_ARPE
и как работает запись в TIM6->ARR когда этот бит установлен?
Genadi Zawidowski
Предлагаю ещё посмотреть внимательно даташит на тему как сбрасыаать флаги прерываний в таймерах...
В Вашем случае возможны потери прерываний (если другие ещё флаги потребуются).

Небольшой пример из моего кода:
Код
    void
    TIM3_IRQHandler(void)
    {
        const uint_fast32_t st = TIM3->SR;
        if ((st & TIM_SR_UIF) != 0)
        {
            TIM3->SR = ~ TIM_SR_UIF;    // clear UIF interrupt request
            handle_timer_imterrupt();
        }
        // проверяем другие флаги в st
    }
Golikov A.
и если еще умничать, то это ШИМом делается, а не через прерывания... хотя думаю на самом деле это просто тестsm.gif
zorromen
Шим не подойдет, т.к. прерывание будет усложнятся и будут добавленны еще условия которые будут зависить от переферии. А какие еще флаги кроме UIF могут быть установленустановлены? Вот думаю может исрользовать режим импульса, по идее он точно один раз попадет в прерывание. Ну а вообще если логически подумать, приведеный код должен формировать импульс, может в коде ошибаюсь? На счет обновления ARR при установленном ARPE, ничего сказать не смогу т.к. начал изучение только недавно, но попробовать можно, сбросить этот бит и после обновить ARR и установить этот бит.
Golikov A.
какие флаги могут быть еще?
Да какие угодно, проект будет жить, развиваться, появятся еще обработчики... да это просто надо взять себе за правило, что регистры для сброса прерывания записываются только одним значением, потому не надо делать
&= или |= вам не нужно начальное состояние регистра, просто равно и все. Это правило! Плюс запись атомарная, в отличии от &= и |=. Если очень хочется могу в очередной раз написать как возникает проблемаsm.gif

Цитата
код должен формировать импульс, может в коде ошибаюсь

Да ошибаетесь, и именно в кодеsm.gif с битами ARPE, вам намекают на буферезированный режим обновления конца счетчика. То есть когда вы пишите обновить регистр вы обновляете его не мгновенно, а только когда таймер досчитает до заданного значения. Вот и получилось что когда вы пишите 100, таймер до ходит до прошлого 20, и только потом начинает считать до 100, а вы ему пишите 20, он дойдет до 100 и заменит на 20, потому у вас все по фазе и съехало...
zorromen
Можете помочь как нужно написать чтобы работало.
Golikov A.
ну либо поменяйте местами присвоения, либо присвояемое, либо уберите флаг буферезированной загрузки.

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

вот что у вас происходит, то есть когда вы в таймер пишите 100 это число на следующих ход, учитывайте это или отключите буфер
zorromen
Но ведь PSC тоже с буфером, и он вроде не отключается, и логично чтобы ARR и PSC обновились одновременно, чувствую что нужно использовать UG, но как не понял.
Если не трогать вообще PSC, то все работает, управляется ARR без буфера, а если нужно менять еще и PSC?

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


у вас сейчас логика
задали режим 1, выставили сигнал 1
дождались прерывания
задали режим 2, выставили сигнал 2
и так далее


а должна быть
задали режим инициализации(просто какой то интервал, чтобы успеть задать настройки), подготовились к режиму 1
дождались прерывания (тут применяться настройки режима 1)
подготовились к режиму 2, задали сигнал 1
дождались прерывания (тут применяться настройки режима 2)
подготовились к режиму 1, задали сигнал 2
и так далее



SSerge
В процессе инициализации полезно "вручную" сгенерировать update event при котором произойдёт загрузка из теневых регистров.
Код
  TIM1->ARR = xxx;
  //и прочие настройки
  TIM1->EGR   = TIM_EGR_UG; // генерируем update event для загрузки из всех теневых регистров
  TIM1->CR1 |= TIM_CR1_CEN;   // разрешаем счёт
zorromen
А если это делать в прерывании, не вызовет ли это прерывание снова? Пока проверить не могу.
zorromen
Упорство победило, если хотят играть по своим правилам, то по ним и с играем. После настройки в прерывании регистров, создаю новое прерывание и ref_register=1, в него заходим сразу после данного прерывания и тем самым обновляем теневые регистры PSC и ARR, и все заработало как нужно. Очень странно видеть, что сколько читал русскую документацию, не все объясняется, только в даташите нашел, что PSC тоже имеет теневой регистр.

Настройка
CODE
TIM6->PSC = 143;
TIM6->ARR = 62499; //100 mS
TIM6->DIER = TIM_DIER_UIE;
TIM6->CR1 |= TIM_CR1_CEN | TIM_CR1_OPM | TIM_CR1_ARPE;

void TIM6_DAC_IRQHandler(void)
{
if (ref_register == 0)
{
switch (UV_state)
{
case 0:
TIM6->PSC = 0;
TIM6->ARR = 0x707;//1799; //20 uS
TIM6->CNT = 0;
TIM6->EGR = TIM_EGR_UG; //Создать прерывание для обновления регистров
ref_register = 1; //Обновить регистры
TIM6->CR1 |= TIM_CR1_CEN;
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_5, GPIO_PIN_SET);
UV_state = 1;
break;

case 1:
TIM6->PSC = 0x008f;//143
TIM6->ARR = 0xF423;//62499; // 100 mS
TIM6->CNT = 0;
ref_register = 1; //Обновить регистры
TIM6->EGR = TIM_EGR_UG; //Создать прерывание для обновления регистров
TIM6->CR1 |= TIM_CR1_CEN;
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_5, GPIO_PIN_RESET);
UV_state = 0;
break;
}
}
else
{
TIM6->SR = 0;
ref_register = 0;
}
}
jcxz
Цитата(zorromen @ Jul 24 2015, 00:41) *
Очень странно видеть, что сколько читал русскую документацию, не все объясняется, только в даташите нашел, что PSC тоже имеет теневой регистр.

"Не читайте по утрам советских газет" © профессор Преображенский biggrin.gif
Golikov A.
надеюсь вы понимаете принципиальную разницу в работе с принудительном обновлении после задания по сравнению со штатной работой.

Цитата
очень странно видеть, что сколько читал русскую документацию, не все объясняется, только в даташите нашел, что PSC тоже имеет теневой регистр.

тут даже комментировать не хочется...
zorromen
Ну принудительное обновления, я как раз и реализовал, а что такое штатная работа, это когда все на оборот, странно такой фигни например не помню когда програмил AVR и lpc2148.
Golikov A.
Цитата
Ну принудительное обновления, я как раз и реализовал, а что такое штатная работа, это когда все на оборот, странно такой фигни например не помню когда програмил AVR и lpc2148.

В АВР вроде не было, а в LPC кажется были буферные регистры

у вас длина импульсов не съезжает от принудительных обновлений?
zorromen
То будет не критично, вообще еще дурной вопрос.
Нужно будет ловить импульс ответа, и измерить его длину, тут особых проблем не обнаружил, но реализация несколько смущает.
Настройка порта на внешнее прерывание, тут настроил на передний фронт, но в самом прерывании после получения переднего фронта перенастраиваю на задний фронт. Сперва делал чтобы прерывание было от обоих фронтов сразу, но зайдя в прерывание тогда не получалось понять какой именно фронт его вызвал, почему-то чтение пина всегда возвращало 0. Сам импульс около 10 mS. Поэтому решил делать с переключением в прерывании.
Код
  /*Configure GPIO pin : PE2 */
  GPIO_InitStruct.Pin = GPIO_PIN_2;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI2_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI2_IRQn);


Код
void EXTI2_IRQHandler(void)
{    
    if ((EXTI->RTSR >> 2) & 1)
        {
                        ...........
            EXTI->RTSR = 0;    //Otkl rising
            EXTI->FTSR = 0x4; //Vkl Filling
        }
        else
        {
                        ...........
         EXTI->RTSR = 4; //Vkl rising
         EXTI->FTSR = 0; //Otkl Filling
        }
    
    EXTI->PR |=0x04;
}
Golikov A.
Цитата
Нужно будет ловить импульс ответа, и измерить его длину, тут особых проблем не обнаружил, но реализация несколько смущает.

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

почему порт возвращал всегда ноль не понятно, наверное не настроено что-то было

Цитата
То будет не критично,

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