Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: STM32. Функция задержки
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Влад Р.
Написал функцию задержки на базовом таймере.
В одной программе работает отлично, но вызывается правда постоянно с одним и тем же значением. В другой то ли работает криво, то ли не работает вообще:
Код
#define APB1_FREQ 24000000

void delay_ms (uint16_t volatile ms) {
  TIM7->CR1 &= ~0xFF;                               // Сбрасываем регистр управления таймера TIM7
  if (ms != 1) {                                    // Если значение задержки НЕ равно 1,
  TIM7->PSC = APB1_FREQ/1000-1;                     // настраиваем таймер TIM7 на период 1 мс,
  TIM7->ARR = ms-1;                                 // установить конечное значение счёта с учетом импульса переполнения;
  } else {                                          // иначе,
      TIM7->PSC = 2*APB1_FREQ/1000-1;               // настраиваем таймер TIM7 на период в 2 раза меньше - 0,5 мс
      TIM7->ARR = 1; }                              // установить конечное значение счёта на 1 с учетом импульса переполнения
  TIM7->CR1 |= (TIM_CR1_OPM | TIM_CR1_CEN);         // Установить режим "одного импульса" и включить таймер TIM7
  while(!(TIM7->SR & TIM_SR_UIF));                  // Дождаться конца задержки
  TIM7->SR &= ~TIM_SR_UIF;                          // Сбросить флаг прерывания
};


Не пойму почему не выдерживается задержка? wacko.gif

ЗЫ. Таймер тактирован в основной программе:
Код
RCC->APB1ENR |= RCC_APB1ENR_TIM7EN;   // Тактируем базовый таймер TIM7
skripach
Ну сделайте вы по людски, что-нибуди вроде:

Код
volatile uint32 ms_timer;

void ms_interrupt(void)
{
  if(ms_timer)
    ms_timer--;
}

void delay_ms (uint32_t ms)
{
  ms_timer=ms;
  while(ms_timer);
}



Влад Р.
Цитата(skripach @ Jun 27 2012, 23:58) *
Ну сделайте вы по людски


За идею спасибо, попробую. Но что "нелюдского" в моем коде?
Сергей Борщ
QUOTE (Влад Р. @ Jun 28 2012, 00:01) *
что "нелюдского" в моем коде?
Вы сбрасываете флаг переполнения после окончания задержки. До следующего вызова функции таймер может дотикать до еще одного переполнения, флаг которого вы и обнаружите при следующем вызове. Сбрасывать флаг нужно пока таймер не тикает (или сразу после программирования периода, если таймер остановить нельзя).

Ну и очищать регистр, про который известно, что старший байт всегда читается как ноль наложением маски ~0xFF неколько нерационально. Почему сразу не записать в него ноль, сэкономив одно чтение этого регистра и промежуточные операции? Аналогично и запись в регистр, содержащий ноль методом наложения маски по "ИЛИ" тоже выглядит "неаккуратненько"
Влад Р.
Цитата(Сергей Борщ @ Jun 28 2012, 00:21) *
Ну и очищать регистр, про который известно, что старший байт всегда читается как ноль наложением маски ~0xFF неколько нерационально. Почему сразу не записать в него ноль, сэкономив одно чтение этого регистра и промежуточные операции? Аналогично и запись в регистр, содержащий ноль методом наложения маски по "ИЛИ" тоже выглядит "неаккуратненько"

Виновен по обоим пунктам. Действительно не по-людски. Переделаю. Но погоды это делать не может.

Цитата(Сергей Борщ @ Jun 28 2012, 00:21) *
Вы сбрасываете флаг переполнения после окончания задержки. До следующего вызова функции таймер может дотикать до еще одного переполнения, флаг которого вы и обнаружите при следующем вызове. Сбрасывать флаг нужно пока таймер не тикает (или сразу после программирования периода, если таймер остановить нельзя).

Дотикать не должен, должен отключиться сразу как только досчитает. Таймер я запускаю с установкой флага TIM_CR1_OPM. "One-pulse mode - counter stops counting at the next update event (clearing CEN bit)." Это ведь так работает?
AHTOXA
Попробуйте после записи значений ARR и PSC сделать
Код
TIM7->EGR |= TIM_EGR_UG;

Это сгенерирует "update event". По нему ARR и прескалер грузятся из предварительного в теневой регистр. То есть, без этого действуют предыдущие значения.

Добавка.
Цитата(Влад Р. @ Jun 28 2012, 01:47) *
Код
    TIM7->PSC = 2*APB1_FREQ/1000-1;               // настраиваем таймер TIM7 на период в 2 раза меньше - 0,5 мс

Здесь вы настроили на период в два раза больше.
Влад Р.
Цитата(AHTOXA @ Jun 28 2012, 00:57) *
Попробуйте после записи значений ARR и PSC сделать
Код
TIM7->EGR |= TIM_EGR_UG;

Это сгенерирует "update event". По нему ARR и прескалер грузятся из предварительного в теневой регистр. То есть, без этого действуют предыдущие значения.


По-моему, это "побочное" действие данной операции и, если таймер не запущен то ARR заносится программой сразу. Могу ошибаться... Поправьте, если не прав.

Цитата(AHTOXA @ Jun 28 2012, 00:57) *
Здесь вы настроили на период в два раза больше.

Деллитель в 2 раза больше, значит частное (частота импульсов таймера) будет в 2 раза меньше. Ага, период в 2 раза больше. Исправим. Я попутал частоту и период...
Влад Р.
Проблема решилась установкой бита ARPE в управляющем регистре сразу после его сброса (буферизирует новое значение ARR в теневом регистре до события UEV):
Код
TIM7->CR1 = TIM_CR1_ARPE | TIM_CR1_OPM;


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

Еще одно: правильно ли я понимаю принцип генерации события таймером. Допустим конечное значение счета ARR=3. Таймер будет считать от 0 до 3 и только на 4-м импульсе возникнет переполнение и будет сгенерировано событие? Или событие сгенерируется уже на 3-м?
AHTOXA
Короче, так.
Регистр PSC обновляется только при update event.
Если ARPE = 1, то ARR тоже обновляется только при update event (иначе сразу).
Так что установка ARPE в 1 вам не поможет, потому что прескалер всё равно буферизован. Сделайте как я написал, и всё заработаетsm.gif
(Update event - происходит при переполнении или вручную, взведением бита UG в регистре EGR.)
sgs
АНТОХА написал все правильно. Есть только один тонкий момент: нельзя запускать таймер сразу после UG. Требуется несколько тактов процессора, чтобы произошла реальная загрузка счетчика. Только после этого заданный интервал отрабатывается правильно. Для этого вполне достаточно оформить загрузку в виде процедуры, а запуск делать вне этой процедуры.
scifi
Странный выбор таймера для задержки, если только в образовательных целях.
Для этих целей лучше подходит SysTick, а ещё лучше - вот этот счётчик циклов процессора (он 32-разрядный, поэтому арифметика с ним простейшая).
Влад Р.
Цитата(AHTOXA @ Jun 28 2012, 06:51) *
Регистр PSC обновляется только при update event.
(Update event - происходит при переполнении или вручную, взведением бита UG в регистре EGR.)


Новый предделитель не будет установлен, пока не произойдет update event, даже если таймер выключен (CEN=0) ? Т.е. даже если таймер выключен, я все равно должен установить бит UG в регистре EGR, чтобы обновить значение предделителя, правильно?

С учетом вышесказанного код преобразился в следующий:
Код
void delay_ms (uint16_t ms) {
  TIM7->PSC = APB1_FREQ/(((ms == 1) ? 2 : 1)*1000)-1;       // Настраиваем таймер TIM7 на период 0.5 мс для задержки в 1 мс
                                                            // и на период 1 мс для всех остальных значений
  TIM7->EGR = TIM_EGR_UG;                                   // Сгенерировать событие для загрузки нового значения предделителя
  TIM7->SR &= ~TIM_SR_UIF;                                  // Сбросить флаг прерывания
  TIM7->ARR = (ms == 1) ? 1 : (ms-1);                       // Установить конечное значение счёта с учетом импульса переполнения
  TIM7->CR1 = TIM_CR1_OPM | TIM_CR1_CEN;                    // Установить режим "одного импульса" и включить таймер TIM7
  while(!(TIM7->SR & TIM_SR_UIF));                          // Дождаться конца задержки
  TIM7->SR &= ~TIM_SR_UIF;                                  // Сбросить флаг прерывания
};


Не реализовано только это:
Цитата(sgs @ Jun 28 2012, 07:36) *
Есть только один тонкий момент: нельзя запускать таймер сразу после UG. Требуется несколько тактов процессора, чтобы произошла реальная загрузка счетчика. Только после этого заданный интервал отрабатывается правильно. Для этого вполне достаточно оформить загрузку в виде процедуры, а запуск делать вне этой процедуры.

Не пойму что имеется в виду "оформить загрузку в виде процедуры". Сделать загрузку в виду отдельной функции?

Цитата(scifi @ Jun 28 2012, 08:39) *
Странный выбор таймера для задержки, если только в образовательных целях.
Для этих целей лучше подходит SysTick, а ещё лучше - вот этот счётчик циклов процессора (он 32-разрядный, поэтому арифметика с ним простейшая).

Почему странный выбор? Простой таймер - для простой задачи. Вот это действительно интересно, обязательно попробую с ним разобраться. Жаль есть он только в M3, да и SysTick в M0 может отсутствовать. А это "минус" для переносимости кода.

Кто с этим может подсказать, из ДШ это не совсем ясно:
Цитата(Влад Р. @ Jun 28 2012, 02:45) *
Еще одно: правильно ли я понимаю принцип генерации события таймером. Допустим конечное значение счета ARR=3. Таймер будет считать от 0 до 3 и только на 4-м импульсе возникнет переполнение и будет сгенерировано событие? Или событие сгенерируется уже на 3-м?

Иными словами, нужно ли в ARR заносить задержку на один импульс меньше желаемой или это лишнее?
AHTOXA
Цитата(Влад Р. @ Jun 28 2012, 17:21) *
Новый предделитель не будет установлен, пока не произойдет update event, даже если таймер выключен (CEN=0) ? Т.е. даже если таймер выключен, я все равно должен установить бит UG в регистре EGR, чтобы обновить значение предделителя, правильно?

Да.
Цитата(Влад Р. @ Jun 28 2012, 17:21) *
С учетом вышесказанного код преобразился в следующий:

Лучше писать в ARR до генерации события (чтобы не зависеть от значения ARPE):
Код
void delay_ms (uint16_t ms) {
  TIM7->PSC = APB1_FREQ/(((ms == 1) ? 2 : 1)*1000)-1;       // Настраиваем таймер TIM7 на период 0.5 мс для задержки в 1 мс
                                                            // и на период 1 мс для всех остальных значений
  TIM7->ARR = (ms == 1) ? 1 : (ms-1);                       // Установить конечное значение счёта с учетом импульса переполнения
  TIM7->EGR = TIM_EGR_UG;                                   // Сгенерировать событие для загрузки нового значения предделителя
  TIM7->SR &= ~TIM_SR_UIF;                                  // Сбросить флаг прерывания
  TIM7->CR1 = TIM_CR1_OPM | TIM_CR1_CEN;                    // Установить режим "одного импульса" и включить таймер TIM7
  while(!(TIM7->SR & TIM_SR_UIF));                          // Дождаться конца задержки
  TIM7->SR &= ~TIM_SR_UIF;                                  // Сбросить флаг прерывания
};

К тому же сброс флага прерывания после генерации события вполне сойдёт за паузу перез запуском таймера (хотя я лично не встречал упоминаний о её необходимости).
Цитата(Влад Р. @ Jun 28 2012, 17:21) *
Иными словами, нужно ли в ARR заносить задержку на один импульс меньше желаемой или это лишнее?

Да, на один меньше. Для задержки на один такт нужно занести значение 0.
Влад Р.
Цитата(AHTOXA @ Jun 28 2012, 19:52) *
Для задержки на один такт нужно занести значение 0.


Да вроде как нельзя так делать, иначе бы я не городил вот эти конструкции:
Код
((ms == 1) ? 2 : 1)

Код
(ms == 1) ? 1 : (ms-1)


Вот фраза из описания регистра TIMx_ARR: "The counter is blocked while the auto-reload value is null"

ЗЫ AHTOXA, огромное спасибо за активную помощь!
AHTOXA
Да, это я лопухнулсяsm.gif Значит, не надо уменьшать на 1.
ViKo
Цитата(AHTOXA @ Jun 28 2012, 21:03) *
Да, это я лопухнулсяsm.gif Значит, не надо уменьшать на 1.

Пытаюсь досконально разобраться с базовыми таймерами.
Считаю, что на 1 уменьшать надо. Практически пока не проверял. В руководстве есть картинки, где показано, как CNT меняется от 0 до 36, при этом написано, что ARR = 0x36. Значит, счетчик считает на 1 больше.
Фраза "The counter is blocked while the auto-reload value is null" вызывает недоумение.
Вообще, описано плохо. Пытаюсь прикинуть, как биты URS, UDIS работают в схеме таймера, и из описания понять невозможно.
AHTOXA
Цитата(ViKo @ Dec 22 2012, 12:53) *
Считаю, что на 1 уменьшать надо. Практически пока не проверял.

А я практически проверилsm.gif
Для генерации 2МГц пришлось в ARR занести значение 36. (При тактовой 72МГц).
vlad_new
Нодо на 1 уменьшать. Я то же практически проверял.
Цитата(AHTOXA @ Dec 22 2012, 12:50) *
А я практически проверилsm.gif
Для генерации 2МГц пришлось в ARR занести значение 36. (При тактовой 72МГц).

Не должно было такое случиться. Где то единичка убежала.

HHIMERA
Цитата(AHTOXA @ Dec 22 2012, 11:50) *
А я практически проверил sm.gif
Для генерации 2МГц пришлось в ARR занести значение 36. (При тактовой 72МГц).

Как оно работает...
Цитата
the counter counts from 0 to the auto-reload value (content of the TIMx_ARR register) – 1, generates a counter overflow event


Цитата
Example update event period
The update event period is calculated as follows:

Update_event = TIM_CLK/((PSC + 1)*(ARR + 1)*(RCR + 1))




ViKo
Из мануала RM0041 на STM32F100
Цитата
The counter counts from 0 to the auto-reload value (contents of the TIMx_ARR register),
then restarts from 0 and generates a counter overflow event.

То, что показал HHIMERA, относится к режиму Center-aligned mode (up/down counting) для других таймеров.
HHIMERA
Цитата(ViKo @ Dec 22 2012, 14:20) *
То, что показал HHIMERA

Ну какие ещё другие таймера в STM32??? )))
AN4013 Application note

STM32F1xx, STM32F2xx, STM32F4xx, STM32L1xx, STM32F30/31/37/38x timer overview

2.2 Time base generator

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

ViKo
Даете противоречивые показания. sm.gif
Цитата(HHIMERA @ Dec 22 2012, 13:32) *
Отнимать единички надобно... т.к. таймер их сам доплюсовывает...

Противоречит
Цитата
the counter counts from 0 to the auto-reload value (content of the TIMx_ARR register) – 1, generates a counter overflow event

Как вы представляете, что таймер считает не до ARR, а до ARR-1 (то есть, вашими словами, отминусовывает), физически?

По руководству пройдитесь поиском "counter counts", найдете, какие таймеры как считают.
P.S. и там не уверен, не разбирался вообще.
HHIMERA
Update_event = TIM_CLK/((PSC + 1)*(ARR + 1)*(RCR + 1))

Update_event = 72 000 000/((0 + 1)*((36-1) + 1)*(0 + 1)) = ???

ViKo
Цитата(HHIMERA @ Dec 22 2012, 14:00) *
...

Словами спросите. Я же сказал, что тоже считаю, что загружать нужно на 1 меньше. И таймер будет считать от 0 до N-1, всего N состояний. У вас (в AN) некорректная запись.
Кстати, такой записи в этом AN нет!
??
HHIMERA
Цитата(ViKo @ Dec 22 2012, 13:50) *
По руководству пройдитесь поиском "counter count", найдете, какие таймеры как считают.

Спасибо, мне не надо... )))
Недавно с Леонидом Ивановичем рассматривали возможность частотомера на STM32 ...
В железе всё прекрасно...
На STM32f100 до 90МГц (выше не пробовал)... на STM32F05X - 110МГц (больше неоткуда было взять)...

P.S: Доки для STM32 писались не новичками... и не для новичков... увы...
Отсюда и сжатый стиль, и "неоднозначные толкования", и пр. ... но в целом - всё достойно и понятно...

Цитата(ViKo @ Dec 22 2012, 14:13) *
У вас (в AN) некорректная запись.

"Мопед не мой"(С)

И в чём некорректность???
ViKo
Цитата(HHIMERA @ Dec 22 2012, 14:18) *
Спасибо, мне не надо... )))
Отсюда и сжатый стиль, и "неоднозначные толкования", и пр. ... но в целом - всё достойно и понятно...

Нихрена там не понятно. И не достойно.
Был бы признателен, если бы растолковали, как работают биты UDIS, URS в базовых таймерах. Чтобы не противоречило ни одной из фраз из руководства. У меня пока никак не складывается картинка. Что на что влияет...?
Или, например, если загружаем в ARR значение меньше прежнего, при ARPE сброшенном, а CNT уже перевалившем за новое (до старого, естественно, не добрался еще). Тут же появится overflow event, или будет до переполнения счетчика скакать?

Цитата(HHIMERA @ Dec 22 2012, 14:20) *
И в чём некорректность???

Еще раз - где вы взяли ту цитату после "Как оно работает": (...) - 1, что привели?

Проверил на своем.
Код
void Timer3_init(void) {
/* Разрешить тактирование Таймера 3 */
  RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
/* Prescaler 8 MHz */
  TIM3->PSC = 24 / 8 - 1;
/* Autoreload 2 MHz */
  TIM3->ARR = 8 / 2 - 1;
/* PWM */
  TIM3->CCMR2 = 0x6060 | TIM_CCMR2_OC4PE | TIM_CCMR2_OC3PE;
/* Противоположная полярность активных сигналов (не обязательно) */
  TIM3->CCER = TIM_CCER_CC4E | TIM_CCER_CC4P | TIM_CCER_CC3E;
/* Регистры сравнения */
  TIM3->CCR3 = TIM3->CCR4 = 8 / 2 / 2;  
/* Разрешить счет */
  TIM3->CR1 = TIM_CR1_ARPE | TIM_CR1_URS | TIM_CR1_CEN;
/* Remap TIM3 to PC8, PC9 */
  AFIO->MAPR = 0x00000c00;  
}

Выдает 2 MHz на светодиодах STM32VLDiscovery. Не забудьте задать альтернативные функции для портов PC8, PC9.
Продолжу с битами, доложу.
HHIMERA
Цитата(ViKo @ Dec 22 2012, 14:28) *
Нихрена там не понятно. И не достойно.

Просто надо привыкнуть к стилю написания документации...
Цитата
Был бы признателен, если бы растолковали

Не-не... я нифига ни разу не учитель-разжеватель и не писатель... Я так... ткнул, пнул и съе... и свалил... сорри...
Цитата
Или, например, если загружаем в ARR значение меньше прежнего, при ARPE сброшенном, а CNT уже перевалившем за новое (до старого, естественно, не добрался еще). Тут же появится overflow event, или будет до переполнения счетчика скакать?

Так грубо я шашкой не махал... таких задач не было... но мысль интересная, спасибо... проверю по желанию и возможности...
А вот новое значение ARR, которое не превысило предыдущее и CNT ещё не перевалил за новое, загружал "на лету"... отрабатывалось по новому значению ARR...
Цитата
Еще раз - где вы взяли ту цитату после "Как оно работает": (...) - 1, что привели?

Да с какого-то первого попавшегося даташита... что под руку попалось...
ViKo
Цитата(HHIMERA @ Dec 22 2012, 14:58) *
Так грубо я шашкой не махал... таких задач не было... но мысль интересная, спасибо... проверю по желанию и возможности...
А вот новое значение ARR, которое не превысило предыдущее и CNT ещё не перевалил за новое, загружал "на лету"... отрабатывалось по новому значению ARR...

Сам проверю. Сравним ответы. sm.gif
Второе понятно. ARPE=0 разрешает обновлять теневой регистр ARR, с которым сравнивается счетчик CNT, сразу после записи.
Цитата
Да с какого-то первого попавшегося даташита... что под руку попалось...

Вот именно. Что я и хотел сказать.
AHTOXA
Цитата(vlad_new @ Dec 22 2012, 15:07) *
Не должно было такое случиться. Где то единичка убежала.

Да, точно, я куда-то не туда посмотрелsm.gif
ViKo
Сочинил задержку подлиннее. Тактовая частота подразумевается 24 MHz.
Код
/*!
*******************************************************************************
* @brief    Wide time delay start, check, wait in 1, 10, 100, 1000 us steps
* @details    Старт, проверка и ожидание окончания задержки в микросекундаx
* @param    US - delay time (1 ... 65 536 000 us)
*/
#define WDELAY_START(US)                        \
  TIM6->PSC = ((US < 65536)? (24 - 1) :                    \
          ((US < 655360)? (240 - 1) :                 \
          ((US < 6553600)? (2400 - 1) : (24000 - 1))));        \
  TIM6->ARR = ((US < 65536)? (US - 1) :                    \
          ((US < 655360)? (US / 10 - 1) :                \
          ((US < 6553600)? (US / 100 - 1) : (US / 1000 - 1))));    \
  TIM6->EGR = TIM_EGR_UG;                        \
  TIM6->SR = 0;                                \
  TIM6->CR1 |= TIM_CR1_CEN;                        \

#define WDELAY_CHECK    (TIM6->SR)
#define WDELAY_WAIT()    while (!TIM6->SR)


К этому коду прилагается инициализация таймера.
Код
void Timer6_init(void) {
  RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;
  TIM6->CR1 = TIM_CR1_ARPE | TIM_CR1_OPM | TIM_CR1_URS;
}
ViKo
Хочу продолжить. Написать универсальную задержку, которая равна
DELAY = (PSC + 1) * (ARR + 1) / FCK_PSC
где
DELAY - задержка в секундах
PSC - значение прескалера, 0 - 65535
ARR - значение регистра автозагрузки, 0 - 65535
FCK_PSC - частота, поступающая на вход прескалера, в герцах
Как бы придумать с помощью #define разложить отношение DELAY * FCK_PSC на множители, чтобы получить точное значение?
Жаль, в препроцессоре невозможны циклы.
NaughtyFreak
Очень наворочено blush.gif

Почему правда для задержки не использовать SysTick?
У меня есть вполне простенькая ф-ия для работы с задержками, там и миллисекунды и микросекунды получаюццо:

CODE
typedef unsigned long int DWORD; //max 4 294 967 295 delay units
#define mS_TICK 168000 // @ core FREQ = 168 MHz
#define uS_TICK 168 // @ core FREQ = 168 MHz

DWORD DelayTime; //global tick variable

void SysTick_Configure_ms (void)
{
SysTick_Config(mS_TICK); // interrupt 1 ms
}

void SysTick_Configure_us (void)
{
SysTick_Config(uS_TICK); // interrupt 1 us
}

void Delay_ms(DWORD time)
{
DelayTime=time;
while (DelayTime!=0){};
}

void Delay_us(DWORD time)
{
DelayTime=time;
while (DelayTime!=0){};
}

void DelayTime_Decrement (void)
{
if (DelayTime!=0)
{
DelayTime--;
}
}

void SysTick_Handler(void)
{
DelayTime_Decrement();
}


И усё. в зависимости от надобности кофигурируете в проге чтобы таймер тикал на мс или мкс пишете сколько отсчитывать. Работает 100%, проверено.
ViKo
Цитата(NaughtyFreak @ Dec 28 2012, 11:17) *
Очень наворочено blush.gif
Почему правда для задержки не использовать SysTick?
И усё. в зависимости от надобности кофигурируете в проге чтобы таймер тикал на мс или мкс пишете сколько отсчитывать. Работает 100%, проверено.

На SysTick у меня тоже есть задержка. Но его лучше оставить для RTOS.
Вопрос скорее учебный, чем практический. Зарядка для ума.
Навороченного в задержке на таймере ничего нет.
Всплывает в сознании термин "рекурсия".
ViKo
Типа такого, но на препроцессоре. rolleyes.gif
CODE
#define FT_MHZ 24
void delay_start(const uint32_t time) {
uint32_t psc, rst, arr;
for (psc = 1; psc < 0x10000; psc++) {
arr = time * FT_MHZ / psc;
rst = time * FT_MHZ % psc;
if ((arr < 0x10000) && (rst == 0)) {
TIM6->PSC = psc - 1;
TIM6->ARR = arr - 1;
TIM6->EGR = TIM_EGR_UG;
TIM6->SR = 0;
TIM6->CR1 |= TIM_CR1_CEN;
return;
}
}
for (psc = 1; psc < 0x10000; psc++) {
arr = (time * FT_MHZ + psc / 2) / psc;
if (arr < 0x10000) {
TIM6->PSC = psc - 1;
TIM6->ARR = arr - 1;
TIM6->EGR = TIM_EGR_UG;
TIM6->SR = 0;
TIM6->CR1 |= TIM_CR1_CEN;
return;
}
}
}

void delay_wait(void) {
while (!TIM6->SR);
}
polyname
Цитата
И усё. в зависимости от надобности кофигурируете в проге чтобы таймер тикал на мс или мкс
прерывание с частотой 1МГц и программный поллинг DelayTime ? Вы случайно не индус ?
И почему вы уверены что доступ к DelayTime атомарный ?
sidy
Поскольку в данной теме много разбирающихся в нюансах таймеров STM32, задам вопрос. Имеется MCU STM32F415. Если при достижении таймером TIM1 переполнения я хочу сбросить таймер TIM4 достаточно ли только записать TIM4->CNT=0? Или же если мне нужно не сбросить а продолжить счет TIM4 с определенного значения Х могу ли я записать TIM4->CNT=X?
Mimik
Всем привет, прошу вашей помощи. Устроился на новую работу, и попался мне на переделку вот такой код :
CODE
class FreqIn
{
static std::vector<FreqIn*>tims;
static int timsNum;

public:
FreqIn(TIM_TypeDef *tim_, IRQn irqnUP_, IRQn irqnCC_, uint16_t chan_, uint32_t busDiv_)
: tim(tim_)
, irqnUP(irqnUP_)
, irqnCC(irqnCC_)
, chan(chan_)
, busDiv(busDiv_)
{
prescaler = 0;
tims.push_back(this);
timsNum ++;
reset();
}

void start()
{
stop();
enable = 1;
}
void stop()
{
enable = 0;
}
void reset()
{
timerInit();
stop();
}

inline float freq() {return freq_;}
inline uint16_t holdEdge() {return holdEdge_;}
inline void setEdge(uint16_t t)
{
timerInit();
holdEdge_ = t;
}
inline uint16_t holdPrescaler() {return prescaler;}
inline void setPrescaler(uint16_t t)
{
//timerInit();
prescaler = t;
}

private:
void timerInit()
{
NVIC_DisableIRQ(irqnUP);
NVIC_DisableIRQ(irqnCC);
TIM_Cmd(tim, DISABLE);

TIM_TimeBaseInitTypeDef timer_base;
TIM_TimeBaseStructInit(&timer_base);
timer_base.TIM_Prescaler = prescaler-1; //1,2,3
timer_base.TIM_Period = 0xFFFF;
TIM_TimeBaseInit(tim, &timer_base);

TIM_ICInitTypeDef TIM_ICStructure;
TIM_ICStructure.TIM_Channel = chan; // канал
if (holdEdge_)
{
TIM_ICStructure.TIM_ICPolarity = TIM_ICPolarity_Falling; // по заднему фронту
}
else
{
TIM_ICStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
}
TIM_ICStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; // прямо с ножки
TIM_ICStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; // без делителя
TIM_ICStructure.TIM_ICFilter = 0; // без фильтра

// эта функция и включает режим PWM input - автоматически настраивает комплементарный канал
// правда в стандартной библиотеке работает на 1 и 2 канале, на 3 и 4 - не умеет
//TIM_PWMIConfig(tim, &TIM_ICStructure);

TIM_ICInit(tim, &TIM_ICStructure);

// Выбираем источник для триггера: вход 1
TIM_SelectInputTrigger(tim, TIM_TS_TI1FP1);
// По событию от триггера счётчик будет сбрасываться.
TIM_SelectSlaveMode(tim, TIM_SlaveMode_Reset);
// Включаем события от триггера
TIM_SelectMasterSlaveMode(tim, TIM_MasterSlaveMode_Enable);

// Разрешаем таймеру генерировать прерывание по захвату
if (chan == TIM_Channel_1) TIM_ITConfig(tim, TIM_IT_CC1, ENABLE);
if (chan == TIM_Channel_2) TIM_ITConfig(tim, TIM_IT_CC2, ENABLE);
if (chan == TIM_Channel_3) TIM_ITConfig(tim, TIM_IT_CC3, ENABLE);
if (chan == TIM_Channel_4) TIM_ITConfig(tim, TIM_IT_CC4, ENABLE);

TIM_ITConfig(tim, TIM_IT_Update, ENABLE);

TIM_ClearFlag(tim, TIM_FLAG_CC1);
TIM_ClearFlag(tim, TIM_FLAG_CC2);
TIM_ClearFlag(tim, TIM_FLAG_CC3);
TIM_ClearFlag(tim, TIM_FLAG_CC4);
TIM_ClearFlag(tim, TIM_IT_Update);

// Включаем таймер
TIM_Cmd(tim, ENABLE);
// разрешаем прерывания
NVIC_SetVector(irqnUP, (uint32_t)irqHandler);
NVIC_SetVector(irqnCC, (uint32_t)irqHandler);
NVIC_EnableIRQ(irqnUP);
NVIC_EnableIRQ(irqnCC);
}

static void irqHandler()
{
for (register int i = 0; i != timsNum; ++i)
{
tims[i]->irq();
}
}

inline void captCh(int ch)
{
}

inline void irq()
{
if (TIM_GetITStatus(tim, TIM_IT_CC1) != RESET) {
TIM_ClearITPendingBit(tim, TIM_IT_CC1);
TIM_ClearITPendingBit(tim, TIM_IT_Update);

cnt = TIM_GetCapture1(tim);
freq_ = float(SystemCoreClock/busDiv)/(float(cnt) + (ovf << 16));
ovf = 0;
}
if (TIM_GetITStatus(tim, TIM_IT_CC2) != RESET) {
TIM_ClearITPendingBit(tim, TIM_IT_CC2);
TIM_ClearITPendingBit(tim, TIM_IT_Update);

cnt = TIM_GetCapture2(tim);
freq_ = float(SystemCoreClock/busDiv)/(float(cnt) + (ovf << 16));
ovf = 0;
}
if (TIM_GetITStatus(tim, TIM_IT_CC3) != RESET) {
TIM_ClearITPendingBit(tim, TIM_IT_CC3);
TIM_ClearITPendingBit(tim, TIM_IT_Update);

cnt = TIM_GetCapture3(tim);
freq_ = float(SystemCoreClock/busDiv)/(float(cnt) + (ovf << 16));
ovf = 0;
}
if (TIM_GetITStatus(tim, TIM_IT_CC4) != RESET) {
TIM_ClearITPendingBit(tim, TIM_IT_CC4);
TIM_ClearITPendingBit(tim, TIM_IT_Update);

cnt = TIM_GetCapture4(tim);
freq_ = float(SystemCoreClock/busDiv)/(float(cnt) + (ovf << 16));
ovf = 0;
}
if (TIM_GetITStatus(tim, TIM_IT_Update) != RESET) {
TIM_ClearITPendingBit(tim, TIM_IT_Update);
ovf++;
}
}

uint32_t ovf;
uint32_t cnt;
uint32_t prescaler;
float freq_;
uint16_t holdEdge_;
uint16_t chan;
uint32_t busDiv;

volatile int enable;
TIM_TypeDef *tim;
IRQn irqnUP;
IRQn irqnCC;
};


это 4 таймера для считывания частоты.
на частотах до 2-3 герц работает отлично, на 32 килогерца показывает точно 320000, но стоит выстваить к примеру 32.101 как частота съезжает и показывается как 32.104 ~ 32.117, на схеме стоит внешний предделитель.

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