_delay_ms()

User avatar
thinker
Уже с Приветом
Posts: 26871
Joined: 29 Aug 2000 09:01

_delay_ms()

Post by thinker »

Вот такой вопрос назрел из области C embedded. Используете ли вы _delay_ms(), например внутри state machine или других циклов, или данная команда считается примером плохого написания кода?
All rights reserved, all wrongs revenged.
User avatar
Medium-rare
Уже с Приветом
Posts: 9195
Joined: 04 Mar 2011 03:04
Location: SFBA

Re: _delay_ms()

Post by Medium-rare »

В программировании есть термин Race Condition, о системе, результат работы которой зависит от времени, или чего-то неподконтрольного. Для того, чтобы не было race conditions, и быть уверенным в правильном результате в системе, в которой какие-то действия совершаются параллельно, используется Synchronization. У синхронизации возможны разные примитивы для обеспечения гарантированного результата, как mutex или critical section, semaphors, всевозможные events/signals и более высокоуровневые штуки вроде conditional variable. Можно сказать, что временная задержка тоже один из тех примитивов, но в большинстве систем ничего не гарантирующий.

И да, это всё зависит от конкретной платформы. В embedded программировании, которое мне немного знакомо, все примитивы синхронизации базировались на стандарте POSIX API, который, например, также в любом Linux. Но даже при полном отсутствии OS и её API, или когда API не доступен, в драйверах, например, синхронизация возможна, современные микропроцессоры содержат примитивы синхронизации сами по себе, инструкции ассемблера, как interlocked exchange/add и прочие.

Знакомая мне State Machine под Linux следила за фазами активации GPS chipset в смартфоне. Специальный host process в embedded Linux ждал сигнала от основной шины питания и/или от таймера, "пинал" шину чипсета, на котором GPS, специфический RF и ещё логика. Там было несколько этапов активации (a) для периодического обновления данных спутников в течении нескольких секунд через каждые полчаса, (b) !спать! от центрального процессора, (с) войти в режим обновления текущей координаты. Всего не помню. Из "времени" участвовал не _delay_ms, а позиксовый таймер. Задержка, это когда ваш поток в вашем процессоре вообще исполняется, его можно затормозить, но зачем. А таймер, если это фича хардверная, даёт по истечении времени сигнал на шину, единицу вместо нуля, от которой "возбуждается" какой-то порт, и вызывается какая-то функция, которая сделает что-то более "умное". Ну и, устройство, тот GPS chipset, тоже выставлял нули и единицы на своей шине с несколькими линиями, мы это "слушали" в том числе осциллографом в процессе отладки. И те самые нули и единицы на шине программного замечательно читались позиксовой коммандой select, которая давала нужный сигнал. Трудно сказать, где там было место именно задержке. Все сигналы были просто переходом с 0 на 1, или наоборот, на шине. Надо поймать, и отработать.
... and even then it's rare that you'll be going there...
DropAndDrag
Уже с Приветом
Posts: 6019
Joined: 11 Mar 2011 05:36

Re: _delay_ms()

Post by DropAndDrag »

я использовал.
не совсем понятно почему это критичено в state_machine или циклах. разве, что ожидание становится неприлично длинным.
в синхронизации между потоками точно не надо использовать, там лучше использовать event-wait.
вот простой пример - оборудование, да хотя бы простое реле, требует задержку. используешь delay и тем временем может быть что-то другое работает.
User avatar
Medium-rare
Уже с Приветом
Posts: 9195
Joined: 04 Mar 2011 03:04
Location: SFBA

Re: _delay_ms()

Post by Medium-rare »

Истечёт задержка, проверил что-то, *не будучи уверенным*, что это что-то не меняется прямо сейчас...
Она ни чем не плоха, задержка, она просто немного для другого. Как, например, анимация.
Для State Machine требуются дискретные состояния, чтобы никаких сомнений не было.

Так серьёзную технику, на задержке *на сколько?* *чего?* *потока state machine, он больше ни на что не реагирует?* *другого потока?* **какие ещё последствия задержки потоков?** не делают.
... and even then it's rare that you'll be going there...
User avatar
thinker
Уже с Приветом
Posts: 26871
Joined: 29 Aug 2000 09:01

Re: _delay_ms()

Post by thinker »

DropAndDrag wrote:не совсем понятно почему это критичено в state_machine или циклах. разве, что ожидание становится неприлично длинным..
Да. Например есть система с кнопками, LEDs, дисплеем, и прочим интерфейсом и одна из задач - требуется одновременно включить 3 LEDs. Они подключены к трем ножкам одного порта. Чтобы не превысить максимальный ток по данному порту, мы включаем один LED, ждём 1 ms, потом его выключаем и включаем другой на 1 ms, потом его выключаем и включаем третий и потом повторяем с первого. Для юзера все три горят одновременно и ярко. В действительности они горят по очереди, каждый по 1 ms.

Имеется Interrupt Service Routine (срабатывающая например каждые 15 микросекунд) , в который есть счетчик. Когда счетчик насчитал 3 ms, мы в state_machine выполняем Refresh_LED() как описано выше. Можно в ней ипользовать _delay_ms(1) и ждaть пока она не закончит, но тогда весь процес будет тормозиться как минимум на 3 ms. А можно не ждать и не использовать delay_ms(1), а установить счетчик на 1 ms и тогда уже включать/выключать по одному LED за каждый проход Refresh_LED(). Наверное второй вариант более правильный.
All rights reserved, all wrongs revenged.
User avatar
Byka
Уже с Приветом
Posts: 277
Joined: 22 Feb 2004 21:23
Location: SPb.RU -> USA.COM

Re: _delay_ms()

Post by Byka »

thinker wrote:
DropAndDrag wrote:не совсем понятно почему это критичено в state_machine или циклах. разве, что ожидание становится неприлично длинным..
Да. Например есть система с кнопками, LEDs, дисплеем, и прочим интерфейсом и одна из задач - требуется одновременно включить 3 LEDs. Они подключены к трем ножкам одного порта. Чтобы не превысить максимальный ток по данному порту, мы включаем один LED, ждём 1 ms, потом его выключаем и включаем другой на 1 ms, потом его выключаем и включаем третий и потом повторяем с первого. Для юзера все три горят одновременно и ярко. В действительности они горят по очереди, каждый по 1 ms.

Имеется Interrupt Service Routine (срабатывающая например каждые 15 микросекунд) , в который есть счетчик. Когда счетчик насчитал 3 ms, мы в state_machine выполняем Refresh_LED() как описано выше. Можно в ней ипользовать _delay_ms(1) и ждaть пока она не закончит, но тогда весь процес будет тормозиться как минимум на 3 ms. А можно не ждать и не использовать delay_ms(1), а установить счетчик на 1 ms и тогда уже включать/выключать по одному LED за каждый проход Refresh_LED(). Наверное второй вариант более правильный.
Невозможно оценить все детали, не зная детали Вашей системы. Исходя из общих соображений, использовать задержку в ISR крайне не приветствуется. Обработчик должен быстро что-то сделать и завершиться.
User avatar
thinker
Уже с Приветом
Posts: 26871
Joined: 29 Aug 2000 09:01

Re: _delay_ms()

Post by thinker »

Byka wrote:Исходя из общих соображений, использовать задержку в ISR крайне не приветствуется. Обработчик должен быстро что-то сделать и завершиться.
Задержка не в ISR, а в теле основной программы (state_machine). Внутри ISR есть только счетчики, типа countLED++, чтобы посчитать временные интервалы и затем на их основе делать Refresh() для светодиодов, опросa кнопок, и любые другие задачи, требующие определенных time slicing.
All rights reserved, all wrongs revenged.
User avatar
Medium-rare
Уже с Приветом
Posts: 9195
Joined: 04 Mar 2011 03:04
Location: SFBA

Re: _delay_ms()

Post by Medium-rare »

Тут ничего по прежнему не ясно, что в наличии в System On Chip. Общие соображения как принцип, а может, и объехать их по обочине?

Если state machine более ничего не отрабатывает в тот интервал времени, то можно и delay(ms), потом действие.
Если state machine таки что-то другое может отрабатывать в ms, то надо таймер поставить, и из него вызвать то действие.
Если таймера в системе нет в принципе, но state machine отрабатывать надо что-то ещё в период ms, то приехали.

Ну, про то, многопотчная ли система, тоже ничего не сказано, возможен ли ещё и другой поток, которому поставить delay(ms), потом действие.
Хотя если возможна установка системного таймера, то система уже многопоточна. Но возможны варианты, что поддерживает OS, что нет.
... and even then it's rare that you'll be going there...
DropAndDrag
Уже с Приветом
Posts: 6019
Joined: 11 Mar 2011 05:36

Re: _delay_ms()

Post by DropAndDrag »

thinker wrote:
DropAndDrag wrote:не совсем понятно почему это критичено в state_machine или циклах. разве, что ожидание становится неприлично длинным..
Да. Например есть система с кнопками, LEDs, дисплеем, и прочим интерфейсом и одна из задач - требуется одновременно включить 3 LEDs. Они подключены к трем ножкам одного порта. Чтобы не превысить максимальный ток по данному порту, мы включаем один LED, ждём 1 ms, потом его выключаем и включаем другой на 1 ms, потом его выключаем и включаем третий и потом повторяем с первого. Для юзера все три горят одновременно и ярко. В действительности они горят по очереди, каждый по 1 ms.

Имеется Interrupt Service Routine (срабатывающая например каждые 15 микросекунд) , в который есть счетчик. Когда счетчик насчитал 3 ms, мы в state_machine выполняем Refresh_LED() как описано выше. Можно в ней ипользовать _delay_ms(1) и ждaть пока она не закончит, но тогда весь процес будет тормозиться как минимум на 3 ms. А можно не ждать и не использовать delay_ms(1), а установить счетчик на 1 ms и тогда уже включать/выключать по одному LED за каждый проход Refresh_LED(). Наверное второй вариант более правильный.
не знаю уж зачем и почему у вас так. предположу, что есть коммутатор, который соединяет 1 вход на 3 (или больше) выхода. может быть можно подцепить TTL 8-ножковую микросхемку 50омный буфер, типа вот такой http://www.microchip.com/wwwproducts/De ... uct=TC4427, чем извращаться в программировании?

если нельзя, то, во-первых, я бы не стал использовать счетчик на 1 ms в Refresh_LED(). лучше использовать компьютерные часы и хранить время последнего переключение (понятно, что горящий LED как-то хранится)
Дальше бы я попробовал уйти от прерывания:
- с генератором прикинул наименьшую частоту при которой незаметно мерцаний. глаз ловит 15 Hz и как бы частота должна быть 30 Hz или ~30 мс горение, но LED имеют послесвечение, так что может быть 50 мс горения будет достаточно.
- потом бы я оценил максимальное время выполнения в любом положении state machine
- если максимальное время выполнения в несколько раз меньше длины наибольшего периода горения, то просто в конце каждого состояния вызывал бы Refresh_LED().

При таком подходе бы четко видите, что вызываете Refresh_LED(). А прерывания - это танцы с бубнами (где-то отдельно Refresh_LED() лежит, а где-то подключение-отключение прерывания в программе. А самое главное - нету связи между machine_state и таким приколом с LEDs.
DropAndDrag
Уже с Приветом
Posts: 6019
Joined: 11 Mar 2011 05:36

Re: _delay_ms()

Post by DropAndDrag »

DropAndDrag wrote:не совсем понятно почему это критичено в state_machine или циклах. разве, что ожидание становится неприлично длинным.
Я неудачно выразился. Есть 2 варианта.
Первый - это когда есть шустрая state_machine, для которой 1 мс критично. Понятно, что ставить 1 мс задержку - это не лезит ни в какие ворота.
Второй - когда 1 мс некритична. Понятно, что я имел ввиду, только этот вариант. Скажем система меряет температура. Да тут и 10 мс - ерунда. Или там где работают реле скорость, которых 1 мс, так что и для системы дополнительная мс не может быть критична или разработчика надо гнать.

Вообщем дело как всегда с низами и верхами. Низы, то бишь OS говорит, я могу выйти в обработчки превывания за 1 микросекунду. А верхи, то бишь система, говорит - это хорошо, но мне это нафиг не надо. Для меня и 1 мс пойдет, но у мне надо, чтобы у процессора производительность была такая, чтобы за 1-2-3 мс отработать код, а еще бы хорошо бы и multithreading иметь, чтобы программа была попроще. (всякие отложенные прерывания или обработку на пониженном уровне я убрал для упрощения)
Так и здесь - что такое 1 мс для вашей подсистемы - ну кто это знает, кроме вас :wink:
User avatar
thinker
Уже с Приветом
Posts: 26871
Joined: 29 Aug 2000 09:01

Re: _delay_ms()

Post by thinker »

DropAndDrag wrote:не знаю уж зачем и почему у вас так. предположу, что есть коммутатор, который соединяет 1 вход на 3 (или больше) выхода. может быть можно подцепить TTL 8-ножковую микросхемку 50омный буфер, чем извращаться в программировании?
ээ, железо пока менять не будем. :-)
DropAndDrag wrote:если нельзя, то, во-первых, я бы не стал использовать счетчик на 1 ms в Refresh_LED(). лучше использовать компьютерные часы и хранить время последнего переключение (понятно, что горящий LED как-то хранится)
Прерывание у меня всё равно будет использоваться для других задач и я не вижу чем лучше "компьютерные часы". Кстати что конкретно вы под этим понимаете? Внутренний отдельный осцилятор, который не связан с основным?
DropAndDrag wrote:Дальше бы я попробовал уйти от прерывания:
- с генератором прикинул наименьшую частоту при которой незаметно мерцаний. глаз ловит 15 Hz и как бы частота должна быть 30 Hz или ~30 мс горение, но LED имеют послесвечение, так что может быть 50 мс горения будет достаточно.
Тут нужно учитывать duty cycle for each LED и тот факт, что требуется включать по очереди 3 диода. Если duty cycle = 33% и каждый диод горит по 15 ms, то уже заметно мигание. Поэтому я выбрал 1 ms, duty cycle = 33%.
DropAndDrag wrote:- потом бы я оценил максимальное время выполнения в любом положении state machine
- если максимальное время выполнения в несколько раз меньше длины наибольшего периода горения, то просто в конце каждого состояния вызывал бы Refresh_LED().
Да, я так и делаю.
DropAndDrag wrote:При таком подходе бы четко видите, что вызываете Refresh_LED(). А прерывания - это танцы с бубнами (где-то отдельно Refresh_LED() лежит, а где-то подключение-отключение прерывания в программе. А самое главное - нету связи между machine_state и таким приколом с LEDs.
Refresh_LED() не отдельно а в теле основного цикла while(1), а прерывание типа вот так:

Code: Select all

ISR(TIMER0_OVF_vect)  //do it every 16 microseconds
{
	countST++; //count time spent for each state
	countLED++;	// count LED ticks,  if countLED=63, it is 1 ms
}
All rights reserved, all wrongs revenged.
DropAndDrag
Уже с Приветом
Posts: 6019
Joined: 11 Mar 2011 05:36

Re: _delay_ms()

Post by DropAndDrag »

странно вы считает про 15 мс. 15 мс горит и 30 мс не горит. значит частота около 33 Гц. Вы разглядите? Даже если поставить 10 мс, как максимум, то и 3 и 5 мс будет нормально.

обычные операционки поддерживают компьютерные часы, у которых точность около 1 мс. также свободные каналы таймера используют, когда нужна точная задержка или задержка меньше разрешения компьютерных часов. в вашем случае время выше крыши.
вот про time functions, где и часы есть https://msdn.microsoft.com/en-us/librar ... s.85).aspx
да вот таймеры https://msdn.microsoft.com/en-us/librar ... s.85).aspx
это в в Windows. подобное есть во многих OS, включая real-time.

если использовать часы, то имеется абсолютный отсчет и можно в случае чего в лог записать. а у ваших счетчиков относительное время и если что-то падает из-за другого места, то будет сложно найти взаимосвязь. а пролететь можно всегда :umnik1: . у вас прерывания никогда не терялись :ROFL:
а потом есть маленький момент ... countLED = 63 и он обнуляется? то есть 2 места пишут - надо еще и синхронизацию обеспечить или насчитаете 62 или 64.

если уж удариться в историю, то я так отсчитывал время на DBK-2 и СМ4 (PDP11). по другому, скажем 20 микросекунд было не отсчитать. А так бралась простая ассемблерная команда и высчитавалось время выполнения. вы что-то такое делаете :crazy:
был еще последовательный ISA КАМАК контроллер в PC286. проверку на поступления ответа от контроллера на команду не сделали, так вот приходилось делать задержку в С, чтобы подождать ответа, а задержка еще зависила и от длины кабеля.
User avatar
thinker
Уже с Приветом
Posts: 26871
Joined: 29 Aug 2000 09:01

Re: _delay_ms()

Post by thinker »

DropAndDrag wrote:странно вы считает про 15 мс. 15 мс горит и 30 мс не горит. значит частота около 33 Гц. Вы разглядите?
Да, я так делал и было заметно мерцание у тех диoдов, которые на моей панели. Возможно у других диодов будет по-другому.
DropAndDrag wrote:обычные операционки поддерживают компьютерные часы, у которых точность около 1 мс.
В моей системе нет операционки. Есть чип, ATmega640, в нем крутится программа ассемблера, предварительно написанная на C.
DropAndDrag wrote:у вас прерывания никогда не терялись
Don't scare me :-)
DropAndDrag wrote:если уж удариться в историю, то я так отсчитывал время на DBK-2 и СМ4 (PDP11). по другому, скажем 20 микросекунд было не отсчитать. А так бралась простая ассемблерная команда и высчитавалось время выполнения. вы что-то такое делаете :crazy:
Ну не совсем так но близко.. Частота осцилятора 16 МHz. Имеется внутренний таймер, один байт, который при достижении 255 переполняется и вызывает прерывание. Prescaler я пока не использую. Значит частота прерывания 16М/255=62745 Hz, или 15.9 микросекунды. Особая точность, типа для LEDs или обработки кнопок пока не нужна.

Спасибo. :fr:
All rights reserved, all wrongs revenged.
DropAndDrag
Уже с Приветом
Posts: 6019
Joined: 11 Mar 2011 05:36

Re: _delay_ms()

Post by DropAndDrag »

ATmega640 ... 64 кбайт памяти :ROFL:
уважаемый, вы для работы или от скуки дома мазохизмом занимаетесь :wink:

six flexible timer/counters with compare modes - возможно счетчики можно собрать последовательно и будем вам счастье - отдельное прерывание через 1 мс. и главное в одном месте!

посмотрев на параметры этого чуда, выскажу крамольную истину - кроме как для использования такой ерунды для передних панелей на утренний мозг ничего не приходит ... вот только бы еще и заках на такие ящики был бы штук ... ну хотя бы от 1000 :food:
swimmer
Уже с Приветом
Posts: 117
Joined: 01 Sep 2004 09:52

Re: _delay_ms()

Post by swimmer »

Посмотрите во что транслируется код ISR на ассемблере. Может оказаться что при периоде таймера 255 клоков ваша программа висит в обработчике 50% времени. Заодно узнаете, остановлен ли счетчик пока вы внутри прерывания, или продолжает считать дальше.

Задействуйте предделитель и заведите прерывание по таймеру с периодом в 1мс. Внутри прерывания будете и LED дергать и при желании антидребезг кнопок организуете. Ели основной программе надо мигнуть, она просто устанавливает флаг, который будет обработан внури ISR и идет дальше. В этом случае никакие задержки типа _delay_ms() внутри основной программы не нужны.
User avatar
thinker
Уже с Приветом
Posts: 26871
Joined: 29 Aug 2000 09:01

Re: _delay_ms()

Post by thinker »

swimmer wrote:Задействуйте предделитель и заведите прерывание по таймеру с периодом в 1мс. Внутри прерывания будете и LED дергать и при желании антидребезг кнопок организуете. Ели основной программе надо мигнуть, она просто устанавливает флаг, который будет обработан внури ISR и идет дальше.
А чем лучше устанавливать обработку внутри прерывания по сравнению с тем как сейчас, когда вся логика и обработка идет в основном цикле while(1), а в прерывании только несколько счетчиков? Меня учили что внутри прерывания нужно иметь как можно меньше кода.
swimmer wrote:В этом случае никакие задержки типа _delay_ms() внутри основной программы не нужны.
Это я уже понял. _delay_ms() наверняка генерирует типа asm("nop"), то есть холостые инструкции, а все остальные процессы в это время висят и ждут.
All rights reserved, all wrongs revenged.
DropAndDrag
Уже с Приветом
Posts: 6019
Joined: 11 Mar 2011 05:36

Re: _delay_ms()

Post by DropAndDrag »

ИМХО, swimmer предлагает изменить подход - глобально :umnik1:

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

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

судя по паре моментов swimmer имел дело с таким зверем и предложил типовой подход к такому зверю. такой неплохой шаблончик вырисовывается, который можно перетаскивать из одной панели в другую ...
swimmer
Уже с Приветом
Posts: 117
Joined: 01 Sep 2004 09:52

Re: _delay_ms()

Post by swimmer »

thinker wrote:Меня учили что внутри прерывания нужно иметь как можно меньше кода.
Если прерывания частые, то в ISR много кода просто не поместится. А так все зависит от задачи.

Например, вы захотите чтобы ваш контроллер работал от батарейки как можно дольше. Тогда в основном цикле может вообще быть практически пусто и все будет происходить по событиям. Выполнил прерывание по нажатию кнопки - уснул в low-power mode, обработал таймер - уснул, принял байт по уарт - уснул. Все происходит по прерываниям, при выходе из прерывания идем спать дальше.
User avatar
thinker
Уже с Приветом
Posts: 26871
Joined: 29 Aug 2000 09:01

Re: _delay_ms()

Post by thinker »

Работа от батарейки не требуется. Я выбрал ISR() на 16 us как минимальное время которое может потребоваться для других задач, не связанных с LEDs. Если увижу, что другие устройства не требуют так часто прерываться или так мало ожидать, то увеличу это время позднее.
All rights reserved, all wrongs revenged.
swimmer
Уже с Приветом
Posts: 117
Joined: 01 Sep 2004 09:52

Re: _delay_ms()

Post by swimmer »

thinker wrote:Работа от батарейки не требуется. Я выбрал ISR() на 16 us как минимальное время которое может потребоваться для других задач, не связанных с LEDs. Если увижу, что другие устройства не требуют так часто прерываться или так мало ожидать, то увеличу это время позднее.
Дело ваше, конечно.

Я бы завел 1мс таймер, сделал управление LED и забыл бы про него. Для задачи более точного измерения интервалов использовал бы второй таймер с мкс разрешением, для следующей задачи, например PWM - третий таймер. У вас там 6 таймеров специально для этого впихнули.
User avatar
Medium-rare
Уже с Приветом
Posts: 9195
Joined: 04 Mar 2011 03:04
Location: SFBA

Re: _delay_ms()

Post by Medium-rare »

Что-то это мне напоминает: http://stackoverflow.com/questions/2838 ... ogram-in-c
... and even then it's rare that you'll be going there...

Return to “Вопросы и новости IT”