• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

ESP8266 и аппаратный таймер hw_timer

pvvx

Активный участник сообщества
Вот девиация длительности входного импульса (голубой) с периодом 100 Гц и выход (желтый):
1607385101476.png
 

pvvx

Активный участник сообщества
Ну и время срабатывания от фронта, с рассогласованием тактовых, т.е. джиттер срабатывания (ESP на 80MHz):
1607385842853.png
и кривая работа выхода GPIO на светодиод на модуле :) как ему бедному тяжко пересилить через резистор (диффузионную) емкость светодиода...
 

pvvx

Активный участник сообщества
Тяжко ему на формирование 5 us импульс, на 10 тоже, т.к. прибавка 1.1..1.8 us на обработку входа в прерывание таймера...
1607386972212.png
 

pvvx

Активный участник сообщества
Для индикаторного масенького (чтоб моргания не видны были) светодиода диммер пойдет... Не более.
 

nikolz

Well-known member
Вот такая запись меня и смущает. Допустим сейчас у меня в основном цикле ничего нет. И я подберу длительность Delta. Далее я решил добавить функционал и подключил 4 датчика Ds1820, у меня изменится длительность loop? Значит опять нужно подбирать значение переменной Delta?
ЗЫ: Делал диммер на AVR с частотой 16 МГц, и никаких проблем. Здесь имеем проц 80 или 160 МГц, и не получается сделать импульс 20 мкс
Нельзя объять необъятное.
------------------
Попробуйте мыслить в рамках решаемой задачи,
а не пытаться придумать случаи, когда диммер перестанет им фактически быть.
------------------------
когда Вы будете разрабатывать устройство, например, управления самогонным аппаратом ( там есть и датчики температуры и диммер) ,
то будете составлять алгоритм для этого конкретного устройства,
а не переделывать диммер в автомат управления самогонным аппаратом.
------------
Есть такое понятие -технология разработки устройства.
----------------------
Прежде чем что-то разрабатывать, Вы должны:

1) сформулировать задачу - т е описать функциональные свойства устройства или, иначе говоря, составить техническое задание.

2) После этого, вы выбираете метод решения задачи,

3) затем разрабатываете алгоритм , реализующий выбранный метод,

лишь после этого Вы пишите этот алгоритм на любом языке программирования затем транслируете компилируете отлаживаете...
---------------
Указанная последовательность действий обладает тем замечательным свойством, что п 1,2,3 - универсальные и не зависят от языка программирования.
Обычно аля-кулибины и самоучки не утруждают себя выполнением этих этапов, так как не умеют их делать.
Но именно эти пункты позволяют на этапе практической реализации сделать все быстро и оптимально.
Написание программы под конкретное железо - это подобно общению на неизвестном языке с помощью словаря.
Именно так и выглядят программы, которые аля-кулибины сразу начинают лепить придумывая на ходу что же они такое лепят.
---------------
Вы не опубликовали свое тех задание, а лишь сообщили название устройства и показали свою программу в которой Вы формируете задержанный от нуля сети импульс.
Я написал Вам простейший алгоритм реализации этого алгоритма - т е диммера для управления яркостью лампочки, либо любой активной нагрузкой.
В диммере лампочки нет надобности высокого быстродействия и высокой точности управления, поэтому нет надобности в железном таймере,
которым в ESP можно формировать импульсы с дискретом на порядок меньше, чем требуется в диммере лампочки.
----------------------
Поэтому прежде чем писать программу сделайте указанные шаги 1 2 3. После этого можно решать надо таймер или нет.
Иначе так и будете гадать, что будет, если Вы чего-то когда-то захотите.
----------------------
Если Вы хотите освоить таймеры ESP, т е освоить управление железом , то причем здесь диммер.
Вам и надо писать тесты железа используя SDK. В этом случае не имеет значение есть датчики температуры или их нет.
----------------------
Пока же Вы все смешали в одну большую кучу
и пытаетесь на ней гадать, а что если...
 

nikolz

Well-known member
Вот такая запись меня и смущает. Допустим сейчас у меня в основном цикле ничего нет. И я подберу длительность Delta. Далее я решил добавить функционал и подключил 4 датчика Ds1820, у меня изменится длительность loop? Значит опять нужно подбирать значение переменной Delta?
ЗЫ: Делал диммер на AVR с частотой 16 МГц, и никаких проблем. Здесь имеем проц 80 или 160 МГц, и не получается сделать импульс 20 мкс
эта проблема решается просто.
Вводите два коэффициента A и В, которые позволяют Вам нормировать величину задержки
Dimmer= A*Delta+B.
А в loop вместо Dimmer-- (вычитание 1) делаете вычитание константы C, что обеспечивает управление квантом изменения задержки.
--------------
в ЗЫ -виновато зеркало
 

nikolz

Well-known member
и еще...
Ваш вопрос (рассуждения) про активную и реактивную нагрузку - не имеет отношение ни к таймеру ни к железу ни к программированию
Это вопрос выбора метода решения задачи.
----------
Для этого надо изучать электротехнику, на не пытаться сформировать на ESP импульс в 20 мкс.
И ваш опыт программирования на AVR вообще не поможет.
-------------
А если захотите управлять самогонным аппаратом, то придется изучать и ПИД регулятор.
-------
А пока наслаждайтесь картинками pvvx.
Созерцание их увлекательно, но бесполезно.
 

Melandr

Member
Доброе утро!
pvvx , не могли бы код, которым формировались вышеуказанные осциллограммы, выложить в тему. Хочу проверить, почему у меня не получилось получить такую маленькую длительность импульса. Пробовал также менять частоту процессора на 160 МГЦ, памяти на 40 МГц, но при 50 мкс в коде, на выходе было минимум было 70 мкч
 

pvvx

Активный участник сообщества
Доброе утро!
pvvx , не могли бы код, которым формировались вышеуказанные осциллограммы, выложить в тему. Хочу проверить, почему у меня не получилось получить такую маленькую длительность импульса. Пробовал также менять частоту процессора на 160 МГЦ, памяти на 40 МГц, но при 50 мкс в коде, на выходе было минимум было 70 мкч
Код только этот https://esp8266.ru/forum/threads/esp8266-i-apparatnyj-tajmer-hw_timer.5369/post-76642
а на вход, для теста его, работает программируемый генератор.
 

pvvx

Активный участник сообщества
Генератором + осел и снял основные параметры выложенного "диммера" при работе ESP на 80MHz:
Задержка от фронта выходного импульса от входного перепада в вверх - 1.8..2.7 us
  • Минимальная формируемая длительность импульса - 5..7 us
  • Максимальная формируемая длительность импульса - 1677721.4 us
  • Джиттер по длительности формируемого импульса - до 0.8 us
  • Дополнение длительности импульса к счетчику в тиках 0.2 us - около 1.1 us
В итоге максимальная частота обработки при самой малой длительности выходного импульса - примерно 70 кГц (процу надо оставить время на обработку других функций :))
И если прикинуть, что формируемый минимальный сигнал не должен дрожать более чем на 1%, то получаем предел в что-то около 10 кГц.
На 100 Гц у нас ждиттер (в 0.9us) будет составлять одну десятитысячную.
 

pvvx

Активный участник сообщества
Для этого надо изучать электротехнику, на не пытаться сформировать на ESP импульс в 20 мкс.
Вы о чем там бубните? ESP через GPIO может формировать импульс в 2/26000000=0.0000000769 сек и кратные ему. Если надо меньше - есть i2s, SPI.
Но это всё убивается проприетарщиной от Espressif - её закрытым кодом и сИкретной документацией на чип.
И ваш опыт программирования на AVR вообще не поможет.
Никакой разницы, только ESP более убог по части таймеров и прочего, особенно по кол-ву багов.
Не пудрите людям мозг своими недоразумениями.
 

pvvx

Активный участник сообщества
Доброе утро!
pvvx , не могли бы код, которым формировались вышеуказанные осциллограммы, выложить в тему. Хочу проверить, почему у меня не получилось получить такую маленькую длительность импульса. Пробовал также менять частоту процессора на 160 МГЦ, памяти на 40 МГц, но при 50 мкс в коде, на выходе было минимум было 70 мкч
Для данного кода в среде Arduino вот минимальная длительность следования NMI по фронту включающего выходной импульс и NMI по таймеру отключающему импульс:
1607417199395.png
ESP на 160 MHz. В таймер грузится 5 us, осел говорит что выходной импульс имеет длительность 5.26..5.75 us. Меньше незя - переполнение стека.
В Arduino процедуры обработки прерываний (сохранения контекста) очень длинные и кривые (до калбаков и обратный выход) - тем и определяется минимальная длительность следования NMI прерываний до переполнения стека.
Все джиттеры относительно импульса сигнала с генератора c периодом в 100 кГц видны на картинке.
Так-же видно как не справляется GPIO2 с отключением штатного светодиода на модуле ESP12 - фронт искажен, при включении не дотягивает до нуля...
Остальное - это сопли от проводов с железными китайскими пимпками в разъемы 2.54 платы ESP12E DEVKIT к которым подключен осел и генератор...

В коде измена только эта строка:
uint32_t pulse_in_0us2 = 5 * 5; // in 0.2 us (0x007fffff max)
 

pvvx

Активный участник сообщества
Ну и побаловаться можно:
C++:
uint32_t volatile pulse_in_0us2 = 5 * 5; // in 0.2 us (0x007fffff max)
....
void loop() {
  uint32_t tmp = pulse_in_0us2;
  if (++tmp > 10000 * 5)
    tmp = 5 * 5;
  pulse_in_0us2 = tmp;
}
1607418653086.png
Всё - модуль ESP отправляется в его нормальное место обитания - в коробку с хламом. "Скетч" вам накалякал - хватит - балуйтесь :p
 

Melandr

Member
pvvx, доброй ночи!
Спасибо за Ваш пример. Разобрался с работой GPIO ESP8266. Хотел бы еще у Вас поинтересоваться. В своем примере Вы объявляете обработчик таймера такой функцией:
ets_isr_attach(ETS_FRC_TIMER1_INUM, hw_test_timer_cb, NULL);
Я использовал и находил в примерах вот такое объявление обработчика аппаратного таймера:
hw_timer_set_func(hw_test_timer_cb);
В чем разница и какое правильное? По поиску не нашел ничего. Заранее спасибо!
 

pvvx

Активный участник сообщества
pvvx, доброй ночи!
Спасибо за Ваш пример. Разобрался с работой GPIO ESP8266. Хотел бы еще у Вас поинтересоваться. В своем примере Вы объявляете обработчик таймера такой функцией:
ets_isr_attach(ETS_FRC_TIMER1_INUM, hw_test_timer_cb, NULL);
Я использовал и находил в примерах вот такое объявление обработчика аппаратного таймера:
hw_timer_set_func(hw_test_timer_cb);
В чем разница и какое правильное? По поиску не нашел ничего. Заранее спасибо!
ets_isr_attach() находится в ROM и всегда есть. А hw_timer_set_func() - это из какой-то либы.
И приведенный пример будет работать в любой среде - ему не нужно ничего кроме описания уже имеющегося в ESP8266.
 

pvvx

Активный участник сообщества
В примере задействуется только внешняя процедура вектора прерывания. Если оно вам нужно и более короткая по времени исполнения, то есть тут:
В итоге для данного примера не нужно даже SDK. Нужен всего транслятор СИ в коды. :)
 

pvvx

Активный участник сообщества
Но не забывайте главного - описанные характеристики не будут обеспечены при работе совместно с WiFi.
с WiFi вы получите ужасный джиттер, и использование программного прерывания, пусть даже NMI, вам не поможет, т.к. реализация многих процедур в ROM разрешает прерывания и в любой момент может быть вызвана процедура драйвера WiFi, что повлечет за собой дичайший джиттер. А переписывать всё вам не даст закрытый код WiFi, т.е. проприетарщина от Espressif.
В итоге реализовать диммер на ESP8266 возможно только используя два чипа ESP8266 - один работает с WiFi, а другой диммером.
И учитывая кол-во глюков самого чипа и кода от Espressif - овчинка выделки не стоит.
Возьмите какой другой чип, а не это г...но.
 

Melandr

Member
И учитывая кол-во глюков самого чипа и кода от Espressif - овчинка выделки не стоит.
Возьмите какой другой чип, а не это г...но.
согласен с Вами на 100%. Попробовал, использую работу напрямую с регистрами сделать импульс 50 мкс. Так у меня спады импульса перемещаются на 10 мкс. Уже возникают мысли поставить ATmega8 для реализации диммера, а ESP использовать в роли вэб-сервера. Но хочу все-таки довести до реализации диммер на ESP, и посмотреть что получится. Хотел еще спросить, в Вашем примере для настройки пинов как вход и выход используется такая конструкция:
gpio_output_set(0, 0, (1 << GPIO_OUT), (1 << GPIO_IN));
GPIO_OUT - используем как выход
GPIO_IN - используем как вход.
А если стоит задача использовать еще один пин как вход, допустим GPIO_IN_1
То как правильно записать?
Так
gpio_output_set(0, 0, (1 << GPIO_OUT), (1 << GPIO_IN) || (1 << GPIO_IN_1));
 

pvvx

Активный участник сообщества
согласен с Вами на 100%. Попробовал, использую работу напрямую с регистрами сделать импульс 50 мкс. Так у меня спады импульса перемещаются на 10 мкс. Уже возникают мысли поставить ATmega8 для реализации диммера, а ESP использовать в роли вэб-сервера. Но хочу все-таки довести до реализации диммер на ESP, и посмотреть что получится. Хотел еще спросить, в Вашем примере для настройки пинов как вход и выход используется такая конструкция:
gpio_output_set(0, 0, (1 << GPIO_OUT), (1 << GPIO_IN));
GPIO_OUT - используем как выход
GPIO_IN - используем как вход.
А если стоит задача использовать еще один пин как вход, допустим GPIO_IN_1
То как правильно записать?
Так
gpio_output_set(0, 0, (1 << GPIO_OUT), (1 << GPIO_IN) || (1 << GPIO_IN_1));
Извините, но современный большой ящик-комп с полу-киловаттными чипами не может шевелить ножками быстрее чем 15 кГц на ядро. Запуск задачи-потока-приложения (переключение контекста и перераспределение ресурсов родителя к потомку в kernel) обслуживают не более 15 тысяч в сек (в среднем на сегмент используемых компов, не сильно устаревших). Если в задачах будете обрабатывать и передавать меж ними по одному байту то это и есть предельный средний трансфер у современных компов - к 15 килобайтам в сек.
 

Melandr

Member
Добрый день! чтобы не плодить новые темы, решил спросить в этой же. Вопрос касается hw_timer. Нашел интересный перевод из FAQ Espressif

Опорной для FRC1 является частота 80 МГц. Коэффициент деления (DIV) может быть сконфигурирован в 1, 16 и 256. Различные коэффициенты деления будут влиять на длительность каждого тика.
FRC1 считает вниз, при этом значение COUNT_VALUE с каждым тиком уменьшается на 1.
FRC1 можно сконфигурировать в режимах auto-feed-mode или non-auto-feed-mode. Auto-feed-mode: когда сработало прерывание, регистр COUNT_VALUE автоматически получит значение из FRC1_LOAD_VALUE, и начнет операцию декрементирования. Non-auto-feed-mode: когда сработает прерывание, регистр COUNT_VALUE будет установлен в максимальное значение 0x7fffff, и операция декремента продолжится.
Могут быть сконфигурированы прерывания FRC от источников FRC1 и NMI interrupt source. Прерывание NMI не может быть маскировано (запрещено) кодом CPU. Прерывание NMI имеет уровень прерывания LEVEL3 в системе прерываний ESP8266, в то время как другие прерывания имеют уровень LEVEL1. Прерывание NMI по приоритету преобладает на любыми другими прерываниями.
Замечание по поводу SDK HW_TIMER: коэффициент деления SDK равен 16, поэтому длительность каждого тика составляет 0.2 мкс. Параметр hw_timer_arm может быть сконфигурирован до микросекунд, с максимальным значением 1677000 мкс.

Вопрос вот в чем. Допустим я инициализировал таймер таким кодом
Код:
  hw_timer_init(FRC1_SOURCE, 0);
  hw_timer_set_func(onTimerISR);
Далее в обработчике внешнего прерывания по входу я постоянно обновляю значение переменной onTimerISR.
Вопрос состоит, как правильно отключить в собственном обработчике прерывание от hw_timer?
Объясню подробнее, как я вижу работу диммера на прерываниях
1. Объявляем два обработчика прерываний.
2. Первый обработчик внешнего прерывания по входу отслеживает переход сетевого напряжения через 0 и подготавливает hw_timer для формирования задержки открытия симистора в начале периода сетевого напряжения.
3. После срабатывания прерывания от hw_timer, проверяем на каком этапе мы находимся (формирование задержки включения симистора, формирование импульса открытия симистора, ожидание прерывания внешнего прерывания по входу), переводим выход управления симистора в высокий уровень (для формирования импульса открытия), и далее из обработчика мы должны запустить hw_timer с временем, необходимым для открытия симистора. Допустим 10-20 мкс.
4. При следующем вызове обработчика прерывания от hw_timer, проверяем что импульс открытия симистора уже был и теперь необходимо ожидать опять переход через "0". Для этого необходимо отключить прерывание от hw_timer. Как это правильно сделать? Задать заведомо больший интервал, чтобы гарантировано раньше пришло внешнее прерывание по входу и в нем уже изменить значение счетного регистра функцией hw_timer_set_func(onTimerISR);
 
Сверху Снизу