Easyelectronics.ru

Электроника для всех
Текущее время: 24 окт 2020, 20:21

Часовой пояс: UTC + 5 часов



JLCPCB – Прототипы печатных плат за $2/5шт. два слоя. $5/5шт. четыре слоя
Крупнейший производитель печатных плат и прототипов. Более 600000 клиентов и свыше 10000 заказов в день!
Получите скидку на почтовую отправку при первом заказе в JLCPCB!

Начать новую тему Ответить на тему  [ Сообщений: 28 ]  На страницу 1, 2  След.
Автор Сообщение
 Заголовок сообщения: Как сделать нормальный delay?
СообщениеДобавлено: 21 фев 2012, 14:29 
Только пришел

Зарегистрирован: 02 фев 2012, 11:03
Сообщения: 22
Подскажите как сделать нормальную задержку. Через цикл получается очень кривая, видел где-то задержку сделанную на ассемблере, но у меня keil выдает ошибку.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 21 фев 2012, 14:53 
Старожил

Зарегистрирован: 03 мар 2010, 14:10
Сообщения: 1514
Откуда: Беларусь, Минск
Примерно так:
Код:
for (unsigned int i = DELAY; i > 0; --i)
  volatile asm("");

Для Keil возможно изменится формат записи ассемблерной вставки, но суть должна остаться та же.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 21 фев 2012, 15:00 
Старожил

Зарегистрирован: 30 апр 2010, 22:56
Сообщения: 1589
Откуда: Киев
Правильно - запустить прерывания по SysTick, в обработчике увеличивать/уменьшать какую-то глобальную переменную. При задержке - высчитывать, какое значение должно получиться и сравнивать.

Возможны вариации с несколькими таймерами и т.п.

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


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 21 фев 2012, 15:34 
Заглядывает иногда

Зарегистрирован: 07 фев 2012, 19:31
Сообщения: 118
Пожертвовать таймер под задержки.
Ниже приводится текст задержки микросекундного диапазона, накладные расходы на запуск 1-3 мкс точный момент окончания задержки можно выловить по прерыванию от конца счета или по опросу переменной с потерей точности еще на приблизительно одну мкс.
Код:
void TIM4_RCC_Configuration(void)
{
  /* TIM4 clock enable */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);

}

void TIM4_NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;

  /* Enable the TIM2 global Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

  NVIC_Init(&NVIC_InitStructure);
}

void TIM4_init(void)
{
  uint16_t PrescalerValue;
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    /* System Clocks Configuration */
  TIM4_RCC_Configuration();
  TIM4_NVIC_Configuration();
  TIM_TimeBaseStructInit ( &TIM_TimeBaseStructure);
  /* Compute the prescaler value */
  PrescalerValue = (uint16_t) (SystemCoreClock / 24000000) - 1;
  /* Time base configuration */
  TIM_TimeBaseStructure.TIM_Period = 65535;
  TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Down;

  TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);

  /* One Pulse Mode selection */
  TIM_SelectOnePulseMode(TIM4, TIM_OPMode_Single);
  TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
  TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);


}

volatile uint8_t delay_end=0;

void TIM4_IRQHandler(void)
{
  if(TIM_GetITStatus(TIM4,TIM_IT_Update)!=RESET)
  {
    TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
   TIM_Cmd(TIM4, DISABLE);
   TIM_SetCounter(TIM4,0);
   delay_end=1;
  }
}

TIM4_delay(uint16_t mks)
{
  delay_end=0;
  TIM_SetCounter(TIM4,mks);
  TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
  TIM_Cmd(TIM4, ENABLE);
  while(delay_end==0)
    continue;
}


void main(void)
{
  TIM4_init();
  while (1)
  {
    TIM4_delay(500);
  }
  return 0;
}



_________________
Прерывание на хальте, осложненное ПДП циклом...


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 21 фев 2012, 15:43 
Только пришел

Зарегистрирован: 02 фев 2012, 11:03
Сообщения: 22
Спасибо за ответ, но требуется задержка около 2 мкс, с регулировкой в сотню наносекунд. Такое вообще реально?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 21 фев 2012, 15:52 
Старожил

Зарегистрирован: 03 мар 2010, 14:10
Сообщения: 1514
Откуда: Беларусь, Минск
А вы посчитайте сколько nop-ов надо на вашу задержку. Может их и стоит использовать?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 21 фев 2012, 15:58 
Заглядывает иногда

Зарегистрирован: 07 фев 2012, 19:31
Сообщения: 118
Ну в принципе можно штук 50-100 нопов забить в ассемблере, которые заканчиваются рет-ом и подобрать необходиую задержку. Но это все равно не даст точности, так как не позволяет учитывать состояние конвейра при входе и выходе с конвейра, а если во время отработки задержки случится прерывание, то это все становится эфемерным, так как не гарантирует постоянства задержки.

_________________
Прерывание на хальте, осложненное ПДП циклом...


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 21 фев 2012, 16:12 
Старожил

Зарегистрирован: 03 мар 2010, 14:10
Сообщения: 1514
Откуда: Беларусь, Минск
Состояние конвейера определено после перехода - о пуст. А кто в здравом уме делает программные задержки с включёнными прерываниями?
Если вам надо выдать например импульс нужной длительности - лучше использовать таймер с аппаратным формированием этого самого импульса.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 21 фев 2012, 16:24 
Заглядывает иногда

Зарегистрирован: 07 фев 2012, 19:31
Сообщения: 118
Задержки не всегда делаются для генерации импульсов. В принципе для генерации ипульса можно использовать приведенный текст, только предделитель необходимо загнать в наносекундный диапазон и настроить подсистему OC таймера, пожертвовать пин с выходом канала таймера 4, но все равно четкой работы, привязанной к наносекундной сетке времени не получить.

_________________
Прерывание на хальте, осложненное ПДП циклом...


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 21 фев 2012, 17:36 
Только пришел

Зарегистрирован: 02 фев 2012, 11:03
Сообщения: 22
Пользовался нопами, писал (__NOP();), но возникла следующая загвоздка: если симулирую, то не важно какая оптимизация, один ноп 42 нс, все работает как надо. Потом заливаю в discovery, и при использовании оптимизации 1-го уровня все работает, но стоит переключить на оптимизацию нулевого уровеня, перестает работать.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 21 фев 2012, 17:48 
Только пришел

Зарегистрирован: 02 фев 2012, 11:03
Сообщения: 22
Поточнее о программе: происходит прерывание, я устанавливаю некоторые биты одного порта, затем через 2 мкс хочу установить другие биты этого порта.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 21 фев 2012, 18:15 
Старожил

Зарегистрирован: 31 янв 2011, 19:17
Сообщения: 437
Откуда: Москва
Мое мнение такое. Если нужно выдерживать точные тайминги, то есть 2 пути:
1) Делать на таймерах и прерываниях. В данном случае, происходит прерывание, устанавливаешь некоторые биты одного порта, конфигурируешь таймер, чтобы сработал через 2 мкс, и его прерывании делаешь что нужно с другими портами.
1) Писать на асме с нопами, только если системная частота близка к частоте того протокола, который эмулируешь. А если системная частота в разы выше, то см. п. 1.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 21 фев 2012, 18:30 
Старожил

Зарегистрирован: 30 апр 2010, 22:56
Сообщения: 1589
Откуда: Киев
Girator писал(а):
Поточнее о программе: происходит прерывание, я устанавливаю некоторые биты одного порта, затем через 2 мкс хочу установить другие биты этого порта.


Может подробнее о целях расскажешь? А то в свое время, например, когда я узнал, что можно 1-wire через uart рулить, у меня просто сдвиг сознания был. Может проще будет какой-то аппаратный блок использовать?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 21 фев 2012, 19:36 
Старожил
Аватара пользователя

Зарегистрирован: 14 фев 2010, 11:46
Сообщения: 299
viewtopic.php?f=35&t=6770&hilit=DS18B20 в конце, идея думаю понятна.

_________________
Американский форум: задал вопрос - получил ответ.
Израильский форум: задал вопрос - получил вопрос.
Русский форум: задал вопрос и потом долго выслушиваешь какой ты мyдак.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 21 фев 2012, 23:52 
Только пришел

Зарегистрирован: 23 дек 2011, 11:36
Сообщения: 6
Girator писал(а):
Спасибо за ответ, но требуется задержка около 2 мкс, с регулировкой в сотню наносекунд. Такое вообще реально?

Например, так:

Код:
void delay_us ( uint32_t us )
{
   volatile uint32_t delay = (us * (SystemCoreClock / 1000000) / 4);
   while (delay--);   
}


SystemCoreClock должен быть глобально определен и заполнен данными из RCC_GetClocksFreq.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 23 фев 2012, 00:10 
Только пришел

Зарегистрирован: 02 фев 2012, 11:03
Сообщения: 22
Спасибо pkm!!! В принципе все уже работает, но хочется немного улучшить прогу. В ассемблере полный ноль, поэтому прошу не материть) Насколько я понял, первая функция (_delay_loop) содержит внутри цикл. Но, когда я вставляю вызов этой функции на переход по ней тратится 166 нс, можно вставлять в программу сразу цикл, а не вызывать его в функции?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 23 фев 2012, 01:06 
Заглядывает иногда

Зарегистрирован: 07 фев 2012, 19:31
Сообщения: 118
Нужно разместить описание функции в начале исходника то есть она должна быть первой.
В теле обьявления функции необходимо разместить волшебное слово inline

__forceinline void delay_us ( uint32_t us )

Этот пример для кейла.

_________________
Прерывание на хальте, осложненное ПДП циклом...


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 26 сен 2012, 15:42 
Только пришел

Зарегистрирован: 14 май 2012, 16:08
Сообщения: 24
Аналогичная проблема: необходимо делать точную маленькую задержку от 4 до 12 мкс.
Почитав похожие темы, порывшись в интернете пришел к следующим вариантам:
1. Использование SysTick
2. Использование BasckTimer
3. Использовать noop

C первым понятно, реализовать могу.

Таймер тоже реализовал, но есть одна загвоздка, хотел обойтись без прерывания, полагал, что когда таймер досчитает до нужного значения поднимется флаг и всё будет отлично. Но флаг не поднимается без включенного прерывания (а с ним и настройкой NVIC и добавленние обработчика прерывания). На настройку всех этих вещей тратится время, которое Я, пока, не знаю как замерить.
В общем проблему с таймером решил так: "while(TIM6->CNT<ms);" (т.е. без добавления прерывания и считывания флагов), но почему-то данный способ мне кажется варварством... хотя Я могу ошибаться.

Теперь третий вариант - это нупы. Давно о них слышал (еще когда с АВР возился). Но на практике использовать не доводилось. Как их вызывать (в асемблере не силен)? Чему равен один нуп? Если их вызывать то наверняка надо в цикле вызывать, а раз в цикле то надо ,наверняка, учитывать еще время на инкремент счетчика циклов.

В итоге какой из вариантов самый точный, и чем это все можно замерить (кроме осциллографа)?
Вожусь с Discovery STM32F4. Программирую в кеиле.


Да и в заключение, где-то нашел следующую функция:

void Delay_ms(uint32_t ms)
{
volatile uint32_t nCount;
RCC_ClocksTypeDef RCC_Clocks;
RCC_GetClocksFreq (&RCC_Clocks);

nCount=(RCC_Clocks.HCLK_Frequency/10000)*ms;
for (; nCount!=0; nCount--);
}

Не могу понять от куда взялось число 10000? Почему не 168000 ? (у меня такая HCLK_Frequency)


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 27 сен 2012, 23:10 
Только пришел

Зарегистрирован: 14 май 2012, 16:08
Сообщения: 24
Полагал что 1 луп это 1 такт процессора. Видать не так =/
использовал взятую выше функцию
Код:
__asm __forceinline void _delay_loop(uint32_t __count)
{
loop   SUBS     r0,r0,#1
      BNE      loop
      BX    lr
}


и вызываю ее так
_delay_loop(32000000);

где 32000000 частота HCLK.

И все же как народ измеряет (для проверки) микро и может даже нано секунды ?

P.S. в ассемблере не силен


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 28 сен 2012, 00:08 
Старожил
Аватара пользователя

Зарегистрирован: 10 фев 2012, 18:04
Сообщения: 827
Откуда: Україна
2Trouyan
Можно таймер и без прерываний. Попробуй его в режиме одиночного запуска использовать.(OPM).
Он в этом режиме сам останавливается, когда досчитает до переполнения. Таким образом ты его настраиваешь на нужное количество тиков, а потом ставишь бит запуска таймера и втупую мониторишь этот же бит, пока он сам не обнулится. Получается максимально точно задать выдержку. В стм8 очень хорошо работало.

С стм32ф4 тоже пробовал, но у меня пока нет 100%-го понимания как правильно у них запускать таймер в этом режиме.
Последовательность, приведенная в доках, если оттуда выбросить руление пином («пейсатели» доки кажут, что режим одиночного запуска нормальные потсоны обязаны юзать для генерации сигналов с четко заданым временем паузы/импульса), то оно не хочет номально запускаться.
Добиться стабильного запуска таймера в режиме OPM без генерации сигнала удалось методом тыка с использованием недокументированной последовательности записи в некоторые регистры таймера. Но может это только я чего недопонял пока, а у тебя все будет гуд. Надо пробовать.

_________________
"Если вы такие умные, что ж вы строем не ходите?"

Легче зажечь одну маленькую свечу, чем постоянно жаловаться на тьму...


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 28 сен 2012, 00:33 
Старожил

Зарегистрирован: 10 авг 2011, 19:26
Сообщения: 632
Код:
volatile uint32_t delay = (us * (SystemCoreClock / 1000000) / 4);

а если включить мозг то сразу станет понятно что использовать деление и умножения в run-time для микросекундных задержек - абсолютно избыточно и ненужно...


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 28 сен 2012, 00:50 
Супермодератор
Аватара пользователя

Зарегистрирован: 23 апр 2010, 12:58
Сообщения: 5893
Откуда: Москва/Саратов
там же все это аппаратно = быстро. Или вы о чем?

_________________
http://d.64h.me/


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 28 сен 2012, 00:58 
Старожил

Зарегистрирован: 10 авг 2011, 19:26
Сообщения: 632
>>там же все это аппаратно = быстро. Или вы о чем?

ну-ну... умножение, деление и сдвиг, плюс загрузка констант. Вполне может набраться на 1..2 мкс, причем неконтролируемых (длительность деления зависит от чисел), что для микросекундных задержек многовато. Не пойму зачем это делать если константу задежки можно считать в compile-time обычным макросом ?
пример задержки с таймером и станд.либе - это вообще ужос, интересно посмотреть на листинг чего там нагенерил компилер...


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 28 сен 2012, 08:43 
Старожил
Аватара пользователя

Зарегистрирован: 14 июн 2011, 14:22
Сообщения: 335
reptile писал(а):
Не пойму зачем это делать если константу задежки можно считать в compile-time обычным макросом ?
... согласен, вполне неплохо получается вот так:
Код:
   XTAL_CLK equ 24000
MACRO
   _delay_us $reg, $time_us
   mov   $reg,#((XTAL_CLK*$time_us)/3000)
01    subs $reg,$reg,#1
   bne   %b01       
   MEND

после чего достаточно написать
Код:
_delay_us r5,2 ; задержка 2 uс


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как сделать нормальный delay?
СообщениеДобавлено: 28 сен 2012, 16:59 
Только пришел
Аватара пользователя

Зарегистрирован: 11 сен 2012, 23:53
Сообщения: 15
Была же статья на эту тему: http://we.easyelectronics.ru/GYUR22/prostoy-start-stm32-taktirovanie-i-zaderzhka.html
Код:
void delay_ms(uint32_t ms) {
   volatile uint32_t nCount;
   RCC_ClocksTypeDef RCC_Clocks;
   RCC_GetClocksFreq (&RCC_Clocks);
   nCount = (RCC_Clocks.HCLK_Frequency/10000)*ms;
   for (; nCount != 0; nCount--);
}

Лично выводил меандр на один из пинов и смотрел осциллографом. Тютелька в тютельку. В миллисекундах.


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 28 ]  На страницу 1, 2  След.


Часовой пояс: UTC + 5 часов


Кто сейчас на конференции

Сейчас этот форум просматривают: Alexrusneft, truppik


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  

Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Русская поддержка phpBB