реклама на сайте
подробности

 
 
3 страниц V   1 2 3 >  
Reply to this topicStart new topic
> STM32. Функция задержки, программные задержки на таймере
Влад Р.
сообщение Jun 27 2012, 19:47
Сообщение #1


Частый гость
**

Группа: Свой
Сообщений: 87
Регистрация: 9-12-10
Пользователь №: 61 511



Написал функцию задержки на базовом таймере.
В одной программе работает отлично, но вызывается правда постоянно с одним и тем же значением. В другой то ли работает криво, то ли не работает вообще:
Код
#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
Go to the top of the page
 
+Quote Post
skripach
сообщение Jun 27 2012, 20:58
Сообщение #2


■ ■ ■ ■
*****

Группа: Свой
Сообщений: 1 100
Регистрация: 9-08-06
Пользователь №: 19 443



Ну сделайте вы по людски, что-нибуди вроде:

Код
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);
}





--------------------
Делай что должен и будь что будет.
Go to the top of the page
 
+Quote Post
Влад Р.
сообщение Jun 27 2012, 21:01
Сообщение #3


Частый гость
**

Группа: Свой
Сообщений: 87
Регистрация: 9-12-10
Пользователь №: 61 511



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


За идею спасибо, попробую. Но что "нелюдского" в моем коде?
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Jun 27 2012, 21:21
Сообщение #4


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



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

Ну и очищать регистр, про который известно, что старший байт всегда читается как ноль наложением маски ~0xFF неколько нерационально. Почему сразу не записать в него ноль, сэкономив одно чтение этого регистра и промежуточные операции? Аналогично и запись в регистр, содержащий ноль методом наложения маски по "ИЛИ" тоже выглядит "неаккуратненько"


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
Влад Р.
сообщение Jun 27 2012, 21:37
Сообщение #5


Частый гость
**

Группа: Свой
Сообщений: 87
Регистрация: 9-12-10
Пользователь №: 61 511



Цитата(Сергей Борщ @ 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)." Это ведь так работает?
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Jun 27 2012, 21:57
Сообщение #6


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Попробуйте после записи значений 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 мс

Здесь вы настроили на период в два раза больше.


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
Влад Р.
сообщение Jun 27 2012, 22:20
Сообщение #7


Частый гость
**

Группа: Свой
Сообщений: 87
Регистрация: 9-12-10
Пользователь №: 61 511



Цитата(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 раза больше. Исправим. Я попутал частоту и период...

Сообщение отредактировал Влад Р. - Jun 27 2012, 22:43
Go to the top of the page
 
+Quote Post
Влад Р.
сообщение Jun 27 2012, 23:45
Сообщение #8


Частый гость
**

Группа: Свой
Сообщений: 87
Регистрация: 9-12-10
Пользователь №: 61 511



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


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

Еще одно: правильно ли я понимаю принцип генерации события таймером. Допустим конечное значение счета ARR=3. Таймер будет считать от 0 до 3 и только на 4-м импульсе возникнет переполнение и будет сгенерировано событие? Или событие сгенерируется уже на 3-м?
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Jun 28 2012, 03:51
Сообщение #9


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Короче, так.
Регистр PSC обновляется только при update event.
Если ARPE = 1, то ARR тоже обновляется только при update event (иначе сразу).
Так что установка ARPE в 1 вам не поможет, потому что прескалер всё равно буферизован. Сделайте как я написал, и всё заработаетsm.gif
(Update event - происходит при переполнении или вручную, взведением бита UG в регистре EGR.)


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
sgs
сообщение Jun 28 2012, 04:36
Сообщение #10


Участник
*

Группа: Участник
Сообщений: 54
Регистрация: 25-01-06
Из: Самара
Пользователь №: 13 578



АНТОХА написал все правильно. Есть только один тонкий момент: нельзя запускать таймер сразу после UG. Требуется несколько тактов процессора, чтобы произошла реальная загрузка счетчика. Только после этого заданный интервал отрабатывается правильно. Для этого вполне достаточно оформить загрузку в виде процедуры, а запуск делать вне этой процедуры.
Go to the top of the page
 
+Quote Post
scifi
сообщение Jun 28 2012, 05:39
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136



Странный выбор таймера для задержки, если только в образовательных целях.
Для этих целей лучше подходит SysTick, а ещё лучше - вот этот счётчик циклов процессора (он 32-разрядный, поэтому арифметика с ним простейшая).
Go to the top of the page
 
+Quote Post
Влад Р.
сообщение Jun 28 2012, 11:21
Сообщение #12


Частый гость
**

Группа: Свой
Сообщений: 87
Регистрация: 9-12-10
Пользователь №: 61 511



Цитата(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 заносить задержку на один импульс меньше желаемой или это лишнее?

Сообщение отредактировал Влад Р. - Jun 28 2012, 15:26
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Jun 28 2012, 16:52
Сообщение #13


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Цитата(Влад Р. @ 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.


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
Влад Р.
сообщение Jun 28 2012, 17:51
Сообщение #14


Частый гость
**

Группа: Свой
Сообщений: 87
Регистрация: 9-12-10
Пользователь №: 61 511



Цитата(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, огромное спасибо за активную помощь!

Сообщение отредактировал Влад Р. - Jun 28 2012, 17:53
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Jun 28 2012, 18:03
Сообщение #15


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Да, это я лопухнулсяsm.gif Значит, не надо уменьшать на 1.


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
ViKo
сообщение Dec 22 2012, 06:53
Сообщение #16


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Цитата(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 работают в схеме таймера, и из описания понять невозможно.
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Dec 22 2012, 08:50
Сообщение #17


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Цитата(ViKo @ Dec 22 2012, 12:53) *
Считаю, что на 1 уменьшать надо. Практически пока не проверял.

А я практически проверилsm.gif
Для генерации 2МГц пришлось в ARR занести значение 36. (При тактовой 72МГц).


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
vlad_new
сообщение Dec 22 2012, 09:07
Сообщение #18


Местный
***

Группа: Участник
Сообщений: 218
Регистрация: 24-06-10
Пользователь №: 58 127



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

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

Go to the top of the page
 
+Quote Post
HHIMERA
сообщение Dec 22 2012, 10:09
Сообщение #19


Местный
***

Группа: Участник
Сообщений: 226
Регистрация: 10-07-09
Пользователь №: 51 126



Цитата(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))




Go to the top of the page
 
+Quote Post
ViKo
сообщение Dec 22 2012, 10:20
Сообщение #20


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Из мануала 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) для других таймеров.
Go to the top of the page
 
+Quote Post
HHIMERA
сообщение Dec 22 2012, 10:32
Сообщение #21


Местный
***

Группа: Участник
Сообщений: 226
Регистрация: 10-07-09
Пользователь №: 51 126



Цитата(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

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

Go to the top of the page
 
+Quote Post
ViKo
сообщение Dec 22 2012, 10:50
Сообщение #22


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Даете противоречивые показания. 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. и там не уверен, не разбирался вообще.
Go to the top of the page
 
+Quote Post
HHIMERA
сообщение Dec 22 2012, 11:00
Сообщение #23


Местный
***

Группа: Участник
Сообщений: 226
Регистрация: 10-07-09
Пользователь №: 51 126



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

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

Go to the top of the page
 
+Quote Post
ViKo
сообщение Dec 22 2012, 11:13
Сообщение #24


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



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

Словами спросите. Я же сказал, что тоже считаю, что загружать нужно на 1 меньше. И таймер будет считать от 0 до N-1, всего N состояний. У вас (в AN) некорректная запись.
Кстати, такой записи в этом AN нет!
??
Go to the top of the page
 
+Quote Post
HHIMERA
сообщение Dec 22 2012, 11:20
Сообщение #25


Местный
***

Группа: Участник
Сообщений: 226
Регистрация: 10-07-09
Пользователь №: 51 126



Цитата(ViKo @ Dec 22 2012, 13:50) *
По руководству пройдитесь поиском "counter count", найдете, какие таймеры как считают.

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

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

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

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

И в чём некорректность???
Go to the top of the page
 
+Quote Post
ViKo
сообщение Dec 22 2012, 11:46
Сообщение #26


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Цитата(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.
Продолжу с битами, доложу.
Go to the top of the page
 
+Quote Post
HHIMERA
сообщение Dec 22 2012, 11:58
Сообщение #27


Местный
***

Группа: Участник
Сообщений: 226
Регистрация: 10-07-09
Пользователь №: 51 126



Цитата(ViKo @ Dec 22 2012, 14:28) *
Нихрена там не понятно. И не достойно.

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

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

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

Да с какого-то первого попавшегося даташита... что под руку попалось...
Go to the top of the page
 
+Quote Post
ViKo
сообщение Dec 22 2012, 12:02
Сообщение #28


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



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

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

Вот именно. Что я и хотел сказать.
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Dec 22 2012, 17:15
Сообщение #29


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



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

Да, точно, я куда-то не туда посмотрелsm.gif


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
ViKo
сообщение Dec 23 2012, 14:16
Сообщение #30


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Сочинил задержку подлиннее. Тактовая частота подразумевается 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;
}
Go to the top of the page
 
+Quote Post
ViKo
сообщение Dec 28 2012, 06:26
Сообщение #31


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Хочу продолжить. Написать универсальную задержку, которая равна
DELAY = (PSC + 1) * (ARR + 1) / FCK_PSC
где
DELAY - задержка в секундах
PSC - значение прескалера, 0 - 65535
ARR - значение регистра автозагрузки, 0 - 65535
FCK_PSC - частота, поступающая на вход прескалера, в герцах
Как бы придумать с помощью #define разложить отношение DELAY * FCK_PSC на множители, чтобы получить точное значение?
Жаль, в препроцессоре невозможны циклы.
Go to the top of the page
 
+Quote Post
NaughtyFreak
сообщение Dec 28 2012, 08:17
Сообщение #32


Частый гость
**

Группа: Участник
Сообщений: 90
Регистрация: 7-10-11
Из: Москва
Пользователь №: 67 593



Очень наворочено 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%, проверено.

Сообщение отредактировал IgorKossak - Dec 28 2012, 08:18
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!!!
Go to the top of the page
 
+Quote Post
ViKo
сообщение Dec 28 2012, 09:10
Сообщение #33


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



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

На SysTick у меня тоже есть задержка. Но его лучше оставить для RTOS.
Вопрос скорее учебный, чем практический. Зарядка для ума.
Навороченного в задержке на таймере ничего нет.
Всплывает в сознании термин "рекурсия".
Go to the top of the page
 
+Quote Post
ViKo
сообщение Dec 28 2012, 14:07
Сообщение #34


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Типа такого, но на препроцессоре. 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);
}
Go to the top of the page
 
+Quote Post
polyname
сообщение Dec 28 2012, 17:44
Сообщение #35


Частый гость
**

Группа: Участник
Сообщений: 147
Регистрация: 18-05-12
Пользователь №: 71 915



Цитата
И усё. в зависимости от надобности кофигурируете в проге чтобы таймер тикал на мс или мкс
прерывание с частотой 1МГц и программный поллинг DelayTime ? Вы случайно не индус ?
И почему вы уверены что доступ к DelayTime атомарный ?

Сообщение отредактировал polyname - Dec 28 2012, 17:46
Go to the top of the page
 
+Quote Post
sidy
сообщение Mar 8 2013, 19:12
Сообщение #36


Местный
***

Группа: Участник
Сообщений: 280
Регистрация: 2-11-08
Пользователь №: 41 333



Поскольку в данной теме много разбирающихся в нюансах таймеров STM32, задам вопрос. Имеется MCU STM32F415. Если при достижении таймером TIM1 переполнения я хочу сбросить таймер TIM4 достаточно ли только записать TIM4->CNT=0? Или же если мне нужно не сбросить а продолжить счет TIM4 с определенного значения Х могу ли я записать TIM4->CNT=X?
Go to the top of the page
 
+Quote Post
Mimik
сообщение Jun 10 2014, 11:08
Сообщение #37





Группа: Новичок
Сообщений: 1
Регистрация: 10-06-14
Пользователь №: 81 922



Всем привет, прошу вашей помощи. Устроился на новую работу, и попался мне на переделку вот такой код :
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, на схеме стоит внешний предделитель.

Подскажите я уже все перекопал, ничего не помогает...

Сообщение отредактировал IgorKossak - Jun 10 2014, 13:09
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!!!
Go to the top of the page
 
+Quote Post

3 страниц V   1 2 3 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 23rd July 2025 - 05:57
Рейтинг@Mail.ru


Страница сгенерированна за 0.01837 секунд с 7
ELECTRONIX ©2004-2016