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

Таймеры

pvvx

Активный участник сообщества
Для понимая приведу упрощенный пример.
На вход аппаратного прерывания ESP32 подаются короткие импульсы частотой 1 кГц, в ответ на которые ESP32 вырабатывает импульс заданной длительности, например 0,75 мсек.
Это называется одновибратор.
И тут случается проблема: в серии поступающих импульсов появляется периодическая помеха частотой 2 кГц.
Что произойдет в этом случае с нашим ESP32?
Ничего не произойдет, если после получения-отработки импульса запущен таймер и запрет на отработку следующего импульса на назначенное время.
Т.е. у вас получатся следящее окно, в активность которого может быть принят импульс.
Требуемый алгоритм описан не полностью и гадать тонкости никому не охота,..
 

pvvx

Активный участник сообщества
Есть идея при поступлении сигнала на вход аппаратного прерывания запускать еще один таймер - для контроля периода входных импульсов.
Но как остановить работу первого прерывания при обнаружении недопустимого повышения частоты входящих сигналов?
Запретить, удалив из списка софт-таймеров (ets_timer_disarm()).

Вообще по прерыванию от пина при дребезге возможно переполнение стека. Причина неправильное написание процедуры обработки (это встречалось во всех средах для ESP)
 

pvvx

Активный участник сообщества
> Это называется одновибратор.
"одновибратор" бывает разным. Срабатывает по импульсу и выдает активный сигнал определенное время - новый импульс в любой момент назначает новое время выдачи активного сигнала. Или игнорирует импульс если во время выдачи активного сигнала
 

pvvx

Активный участник сообщества
Все варианты описываются за пять секунд. Писать и разъяснять текстом значительно дольше.

1) В прерывании по импульсу выход устанавливается в активное состояние и запускается таймер с нужным периодом активности. В процедуре таймера снимается активный сигнал.
2) В прерывании по импульсу выход устанавливается в активное состояние и запускается таймер с нужным периодом активности, запрещается прерывание по входному импульсу. В процедуре таймера снимается активный сигнал и разрешается прерывание по входному импульсу.
 

ART_HA

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

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

pvvx

Активный участник сообщества
Если бы такой запрет существовал, я бы этот вопрос не задавал. Но такой запрет в принципе недопустим.
? Это кто вам сказал? Ясли сад из Arduino?
Проблемы в Arduino в том, что там всё писано криво и рассчитано на мигание светодиодом в секунды.
Быстрее, используя C++ с распределением памяти с запретом прерываний недопустимо. И так-же без MMU работа в C++ приводит к дефрагментации Heap, что грозит неизбежным падением-вылетом-перезагрузкой.

И
Если у вас нет возможности изменить приоритет прерываний, то в прерываниях генерируете событие, а в общем цикле их обрабатываете.

Событие можно тупо описать с помощью изменения volatile переменной в прерываниях и опрашиваемой в общем цикле.

Получите увеличенный джиттер, но для ваших времен в десятки ms это не критично.
 

pvvx

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

pvvx

Активный участник сообщества
Код:
        ets_isr_mask(1 << ETS_GPIO_INUM); // запрет прерываний GPIOs
        gpio_pin_intr_state_set(GPIO_TEST1, GPIO_PIN_INTR_DISABLE); // смена флагов для контроллера GPIO
        GPIO_INTR_INIT = 0; // сброс INT в контроллере прерываний
Arduino позволяет встраивать хоть ассемблерные вставки...
Тем более ваша задача решается на уровне вызова процедур из ROM чипа, без всяких Arduino либ.
 

pvvx

Активный участник сообщества
Это технические требования моего проекта.
И пожалуйста, не зафлуживайте более тему.
Эта тема о том как вы выпрашиваете на халяву готовый код и решение для вас? Другое не принимается и считается флудом?

Для потребителя ART_HA скетч с двумя вариантами работы:
1669674073310.png
1669674082173.png
C++:
#include "c_types.h"
#include "os_type.h"
#include "eagle_soc.h"
#include "osapi.h"
#include "gpio.h"

#define RestartOnAnyImpulse 1
/*
   по сигналу на ноге МК должен запускаться таймер0, который по истечении заданного времени должен запускать таймер1 и установку одного из выходов МК в HIGH.
   Таймер1 после истечении заданного времени должен сбрасывать ранее установленного выхода МК в LOW.
*/

#define GPIO_INP  4
#define GPIO_OUT  5

#define interval_t0_ms 10
#define interval_t1_ms 25

ETSTimer timer0, timer1;

void IRAM_ATTR gpioIsr(void *par1, void *par2) {
  (void) par1; (void) par2;
  uint32_t status = GPIE;
  GPIEC = status; // clear gpio interrupts
  if (status != 0) {
#if ResetOnAnyImpulse
    ets_isr_mask(1 << ETS_GPIO_INUM);
#endif
    os_timer_arm(&timer0, interval_t0_ms, 0);
    digitalWrite(GPIO_OUT, LOW);
  }
}
void IRAM_ATTR timer0_callback(void *par) {
  (void) par;
  digitalWrite(GPIO_OUT, HIGH);
  os_timer_arm(&timer1, interval_t1_ms, 0);
}
void IRAM_ATTR timer1_callback(void *par) {
  (void) par;
  digitalWrite(GPIO_OUT, LOW);
  gpio_pin_intr_state_set(GPIO_INP, GPIO_PIN_INTR_POSEDGE); // GPIO_PIN_INTR_POSEDGE | GPIO_PIN_INTR_NEGEDGE | GPIO_PIN_INTR_ANYEDGE | GPIO_PIN_INTR_LOLEVEL | GPIO_PIN_INTR_HILEVEL| GPIO_PIN_INTR_DISABLE
#if !ResetOnAnyImpulse
  uint32_t status = GPIE;
  GPIEC = status; // clear gpio interrupts
  ets_isr_unmask(1 << ETS_GPIO_INUM);
#endif
}

void setup() {
  os_timer_setfn(&timer0, timer0_callback, NULL);
  os_timer_setfn(&timer1, timer1_callback, NULL);
  pinMode(GPIO_INP, INPUT);
  pinMode(GPIO_OUT, OUTPUT);
  digitalWrite(GPIO_OUT, LOW);
  ets_isr_mask(1 << ETS_GPIO_INUM);
  gpio_pin_intr_state_set(GPIO_INP, GPIO_PIN_INTR_POSEDGE); // GPIO_PIN_INTR_POSEDGE | GPIO_PIN_INTR_NEGEDGE | GPIO_PIN_INTR_DISABLE
  ets_isr_attach(ETS_GPIO_INUM, gpioIsr, NULL);
  ets_isr_unmask(1 << ETS_GPIO_INUM);
}

void loop() {
  // put your main code here, to run repeatedly:
}
 

pvvx

Активный участник сообщества
Мне кажется, что это какой-то флуд:
Если бы такой запрет существовал, я бы этот вопрос не задавал. Но такой запрет в принципе недопустим.

Однако лазанье в инете привело меня к выводу, что я столкнулся с неразрешимой проблемой ESP32.
Единственный вариант - программный сброс, который меня не устраивает.
Поэтому вынужден снять свой вопрос о сбросе обработчика.
Счас попробовал какой будет джиттер на аппаратном таймере, если применить немного модифицированный код ранее выложенного мной примера esp8266dimmer.ino.
Вышло такое, при 10 us пауза и 25 us импульс, CPU CLK 160 MHz:
1669705750983.png

И режим со сбросом так-же работает:
1669705965472.png
Желтый - входной "импульс", Голубой - выход c ESP8266.
Безусловно есть запаздывание выхода от входа на пару мкс.

C++:
#include "c_types.h"
#include "os_type.h"
#include "eagle_soc.h"
#include "osapi.h"
#include "gpio.h"

#define RestartOnAnyImpulse 1

/*
   по сигналу на ноге МК должен запускаться таймер0, который по истечении заданного времени должен запускать таймер1 и установку одного из выходов МК в HIGH.
   Таймер1 после истечении заданного времени должен сбрасывать ранее установленного выхода МК в LOW.
*/

#define GPIO_INP  4
#define GPIO_OUT  5

#define interval_t0_us 10
#define interval_t1_us 25

volatile uint32_t stage = 0;
void IRAM_ATTR gpio_intr_handler(void *par1, void *par2) {
  (void) par1; (void) par2;
  volatile uint32_t tmp_reg = GPIE;
  GPIEC = tmp_reg; // clear gpio interrupts
  if (tmp_reg & BIT(GPIO_INP)) {
    GPOC = BIT(GPIO_OUT); // digitalWrite(GPIO_OUT, LOW)
#if !RestartOnAnyImpulse
    ets_isr_mask(BIT(ETS_GPIO_INUM));
#endif
    T1I = 0;
    T1C = 0;
    stage = 0;
    T1L = interval_t0_us * 5;
    T1C = 4 | BIT(7); // TM_DIVDED_BY_16 | TM_ENABLE_TIMER | TM_EDGE_INT;
  }
}
void IRAM_ATTR timer1_intr_handler(void *par1, void *par2) {
  (void) par1; (void) par2;
  T1I = 0;
  T1C = 0;
  if (!stage) {
    GPOS = BIT(GPIO_OUT); // digitalWrite(GPIO_OUT, HIGH)
    T1L = interval_t1_us * 5;
    T1C = 4 | BIT(7); // TM_DIVDED_BY_16 | TM_ENABLE_TIMER
    stage = 1;
    gpio_pin_intr_state_set(GPIO_INP, GPIO_PIN_INTR_POSEDGE);
  } else {
    GPOC = BIT(GPIO_OUT); // digitalWrite(GPIO_OUT, LOW)
    stage = 0;
#if !RestartOnAnyImpulse
    GPIEC = GPIE; // clear gpio interrupts
    ets_isr_unmask(BIT(ETS_GPIO_INUM));
#endif
  }
}

void setup() {
  // Init Pins
  pinMode(GPIO_INP, INPUT);
  pinMode(GPIO_OUT, OUTPUT);
  GPOC = BIT(GPIO_OUT); // digitalWrite(GPIO_OUT, LOW)

  // Init Timer1
  ets_isr_mask(BIT(ETS_FRC_TIMER1_INUM) | BIT(ETS_GPIO_INUM)); // запретить прерывания GPIOs & Timer0
  T1C = 0;
  T1L = 0;
  T1I = 0;
  ets_isr_attach(ETS_FRC_TIMER1_INUM, timer1_intr_handler, NULL);
  ESP8266_DREG(4) |= BIT(1); // INTC_EDGE_EN: 0x3FF00004; bit0: WDT edge int enable; bit1: TIMER1 edge int enable
#ifdef TIMER1_USE_NMI_VECTOR
  ESP8266_DREG(0) |= 0x0F; // нет поддержки в данной реализации Arduino, а обработчик входа и таблица векторов погрязли в бинарной либе!
#else
  ESP8266_DREG(0) = 0; // DPORT_BASE = 0
#endif

  // Init gpio int
  ets_isr_mask(BIT(ETS_GPIO_INUM));
  gpio_pin_intr_state_set(GPIO_INP, GPIO_PIN_INTR_POSEDGE);
  ets_isr_attach(ETS_GPIO_INUM, gpio_intr_handler, NULL);

  ets_isr_unmask(BIT(ETS_FRC_TIMER1_INUM) | BIT(ETS_GPIO_INUM)); // разрешить прерывания GPIOs & Timer0
}

void loop() {
}
 

pvvx

Активный участник сообщества
И "подергивания" всегда присутствуют - ведь это Arduino и какая-то бяка на C++ запрещает прерывания в нутре кода вызывающего никому не нужный loop() :p
 

ART_HA

Member
Вопрос интересный...
Задачу решил введением зоны нечувствительности, а именно: по аппаратному прерыванию таймер запускается только в том случае, если его длительность находится в разрешенных пределах.
А раз в случае превышения допустимого значения длительности таймер 1 не запускается, то не вызываются и прерывания 2 и 3.
Т.к. при каждом аппаратном прерывании происходит сброс выходного сигнала полагаю, что этого будет вполне достаточно для решения проблемы.
 
Сверху Снизу