|
STM32. Функция задержки, программные задержки на таймере |
|
|
|
Jun 27 2012, 19:47
|
Частый гость
 
Группа: Свой
Сообщений: 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; // Сбросить флаг прерывания }; Не пойму почему не выдерживается задержка? ЗЫ. Таймер тактирован в основной программе: Код RCC->APB1ENR |= RCC_APB1ENR_TIM7EN; // Тактируем базовый таймер TIM7
|
|
|
|
|
Jun 27 2012, 20:58
|
■ ■ ■ ■
    
Группа: Свой
Сообщений: 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); }
--------------------
Делай что должен и будь что будет.
|
|
|
|
|
Jun 27 2012, 21:01
|
Частый гость
 
Группа: Свой
Сообщений: 87
Регистрация: 9-12-10
Пользователь №: 61 511

|
Цитата(skripach @ Jun 27 2012, 23:58)  Ну сделайте вы по людски За идею спасибо, попробую. Но что "нелюдского" в моем коде?
|
|
|
|
|
Jun 27 2012, 21:21
|

Гуру
     
Группа: Модераторы
Сообщений: 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)
|
|
|
|
|
Jun 27 2012, 21:37
|
Частый гость
 
Группа: Свой
Сообщений: 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)." Это ведь так работает?
|
|
|
|
|
Jun 27 2012, 21:57
|

фанат дивана
     
Группа: Свой
Сообщений: 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 мс Здесь вы настроили на период в два раза больше.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Jun 27 2012, 22:20
|
Частый гость
 
Группа: Свой
Сообщений: 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
|
|
|
|
|
Jun 27 2012, 23:45
|
Частый гость
 
Группа: Свой
Сообщений: 87
Регистрация: 9-12-10
Пользователь №: 61 511

|
Проблема решилась установкой бита ARPE в управляющем регистре сразу после его сброса (буферизирует новое значение ARR в теневом регистре до события UEV): Код TIM7->CR1 = TIM_CR1_ARPE | TIM_CR1_OPM; Но в чем косяк был до этого все равно не пойму  Вроде должно было и без него работать. Почему этот бит так важен для этой функции, на чем функцию клинило без него? Возможно спецы подскажут? Еще одно: правильно ли я понимаю принцип генерации события таймером. Допустим конечное значение счета ARR=3. Таймер будет считать от 0 до 3 и только на 4-м импульсе возникнет переполнение и будет сгенерировано событие? Или событие сгенерируется уже на 3-м?
|
|
|
|
|
Jun 28 2012, 03:51
|

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

|
Короче, так. Регистр PSC обновляется только при update event. Если ARPE = 1, то ARR тоже обновляется только при update event (иначе сразу). Так что установка ARPE в 1 вам не поможет, потому что прескалер всё равно буферизован. Сделайте как я написал, и всё заработает  (Update event - происходит при переполнении или вручную, взведением бита UG в регистре EGR.)
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Jun 28 2012, 04:36
|
Участник

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

|
АНТОХА написал все правильно. Есть только один тонкий момент: нельзя запускать таймер сразу после UG. Требуется несколько тактов процессора, чтобы произошла реальная загрузка счетчика. Только после этого заданный интервал отрабатывается правильно. Для этого вполне достаточно оформить загрузку в виде процедуры, а запуск делать вне этой процедуры.
|
|
|
|
|
Jun 28 2012, 11:21
|
Частый гость
 
Группа: Свой
Сообщений: 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
|
|
|
|
|
Jun 28 2012, 16:52
|

фанат дивана
     
Группа: Свой
Сообщений: 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.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Jun 28 2012, 17:51
|
Частый гость
 
Группа: Свой
Сообщений: 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
|
|
|
|
|
Dec 22 2012, 06:53
|

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

|
Цитата(AHTOXA @ Jun 28 2012, 21:03)  Да, это я лопухнулся  Значит, не надо уменьшать на 1. Пытаюсь досконально разобраться с базовыми таймерами. Считаю, что на 1 уменьшать надо. Практически пока не проверял. В руководстве есть картинки, где показано, как CNT меняется от 0 до 36, при этом написано, что ARR = 0x36. Значит, счетчик считает на 1 больше. Фраза "The counter is blocked while the auto-reload value is null" вызывает недоумение. Вообще, описано плохо. Пытаюсь прикинуть, как биты URS, UDIS работают в схеме таймера, и из описания понять невозможно.
|
|
|
|
|
Dec 22 2012, 09:07
|
Местный
  
Группа: Участник
Сообщений: 218
Регистрация: 24-06-10
Пользователь №: 58 127

|
Нодо на 1 уменьшать. Я то же практически проверял. Цитата(AHTOXA @ Dec 22 2012, 12:50)  А я практически проверил  Для генерации 2МГц пришлось в ARR занести значение 36. (При тактовой 72МГц). Не должно было такое случиться. Где то единичка убежала.
|
|
|
|
|
Dec 22 2012, 10:09
|
Местный
  
Группа: Участник
Сообщений: 226
Регистрация: 10-07-09
Пользователь №: 51 126

|
Цитата(AHTOXA @ Dec 22 2012, 11:50)  А я практически проверил Для генерации 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 periodThe update event period is calculated as follows:
Update_event = TIM_CLK/((PSC + 1)*(ARR + 1)*(RCR + 1))
|
|
|
|
|
Dec 22 2012, 10:32
|
Местный
  
Группа: Участник
Сообщений: 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
Отнимать единички надобно... т.к. таймер их сам доплюсовывает...
|
|
|
|
|
Dec 22 2012, 10:50
|

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

|
Даете противоречивые показания.  Цитата(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. и там не уверен, не разбирался вообще.
|
|
|
|
|
Dec 22 2012, 11:20
|
Местный
  
Группа: Участник
Сообщений: 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) некорректная запись. "Мопед не мой"(С) И в чём некорректность???
|
|
|
|
|
Dec 22 2012, 11:46
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 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. Продолжу с битами, доложу.
|
|
|
|
|
Dec 22 2012, 11:58
|
Местный
  
Группа: Участник
Сообщений: 226
Регистрация: 10-07-09
Пользователь №: 51 126

|
Цитата(ViKo @ Dec 22 2012, 14:28)  Нихрена там не понятно. И не достойно. Просто надо привыкнуть к стилю написания документации... Цитата Был бы признателен, если бы растолковали Не-не... я нифига ни разу не учитель-разжеватель и не писатель... Я так... ткнул, пнул и съе... и свалил... сорри... Цитата Или, например, если загружаем в ARR значение меньше прежнего, при ARPE сброшенном, а CNT уже перевалившем за новое (до старого, естественно, не добрался еще). Тут же появится overflow event, или будет до переполнения счетчика скакать? Так грубо я шашкой не махал... таких задач не было... но мысль интересная, спасибо... проверю по желанию и возможности... А вот новое значение ARR, которое не превысило предыдущее и CNT ещё не перевалил за новое, загружал "на лету"... отрабатывалось по новому значению ARR... Цитата Еще раз - где вы взяли ту цитату после "Как оно работает": (...) - 1, что привели? Да с какого-то первого попавшегося даташита... что под руку попалось...
|
|
|
|
|
Dec 22 2012, 12:02
|

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

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

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 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; }
|
|
|
|
|
Dec 28 2012, 08:17
|
Частый гость
 
Группа: Участник
Сообщений: 90
Регистрация: 7-10-11
Из: Москва
Пользователь №: 67 593

|
Очень наворочено Почему правда для задержки не использовать 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] - для короткого!!!
|
|
|
|
|
Dec 28 2012, 09:10
|

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

|
Цитата(NaughtyFreak @ Dec 28 2012, 11:17)  Очень наворочено Почему правда для задержки не использовать SysTick? И усё. в зависимости от надобности кофигурируете в проге чтобы таймер тикал на мс или мкс пишете сколько отсчитывать. Работает 100%, проверено. На SysTick у меня тоже есть задержка. Но его лучше оставить для RTOS. Вопрос скорее учебный, чем практический. Зарядка для ума. Навороченного в задержке на таймере ничего нет. Всплывает в сознании термин "рекурсия".
|
|
|
|
|
Dec 28 2012, 17:44
|
Частый гость
 
Группа: Участник
Сообщений: 147
Регистрация: 18-05-12
Пользователь №: 71 915

|
Цитата И усё. в зависимости от надобности кофигурируете в проге чтобы таймер тикал на мс или мкс прерывание с частотой 1МГц и программный поллинг DelayTime ? Вы случайно не индус ? И почему вы уверены что доступ к DelayTime атомарный ?
Сообщение отредактировал polyname - Dec 28 2012, 17:46
|
|
|
|
|
Jun 10 2014, 11:08
|
Группа: Новичок
Сообщений: 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] - для короткого!!!
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|