• Уважаемые посетители сайта esp8266.ru!
    Мы отказались от размещения рекламы на страницах форума для большего комфорта пользователей.
    Вы можете оказать посильную поддержку администрации форума. Данные средства пойдут на оплату услуг облачных провайдеров для сайта esp8266.ru
  • Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

OTA и прерывания

pvvx

Активный участник сообщества
И где-же наш nikolz - он несколько раз, год назад и т.д. писал, что у него сделано включение реле по переходу фронта в сети для снижения помех и прочие диммеры на ESP8266, и типа работает... Вот только итогов нема - одна лапша на уши, как и от enjoynering :) Но с enjoynering понятно - в сети нет скетча c правильно работающим "dimmer"...
 

Melandr

Member
pvvx, а не намекнете, как режим вывода меняется для ESP8266.
Код:
  gpio_init();
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
В режиме выхода
gpio_output_set(0, 0, (1 << TRIAC_PIN), 0);
Низкий уровень
GPIO_OUT_W1TC = BIT(TRIAC_PIN);
При этом, что самое интересное. Сделал скетч, который переключает вывод управления симистором в цикле, и все нормально
Код:
#include "ESP8266WiFi.h"
#include "ESP8266mDNS.h"
#include "WiFiUdp.h"
#include "ArduinoOTA.h"

#include "ets_sys.h"
#include "gpio.h"

#define GPIO_OUT_W1TS (*(volatile uint32_t *)0x60000304)
#define GPIO_OUT_W1TC (*(volatile uint32_t *)0x60000308)
#define GPIO_STATUS (*(volatile uint32_t *)0x6000031C)
#define GPIO_STATUS_W1TC (*(volatile uint32_t *)0x60000324)

// Replace with your network credentials
const char* ssid = "ASUS";
const char* password = "gCU8YNZs";

const int ESP_BUILTIN_LED = 2;

void setup() {
  gpio_init();
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);

  gpio_output_set(0, 0, (1 << ESP_BUILTIN_LED), 0);
  GPIO_OUT_W1TC = BIT(ESP_BUILTIN_LED);

  Serial.begin(230400);
  Serial.println("Booting");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(500);
    ESP.restart();
  }

  ETS_GPIO_INTR_DISABLE();

  ArduinoOTA.onStart([]() {
    Serial.println("Start");
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });

  ArduinoOTA.begin();

  pinMode(ESP_BUILTIN_LED, OUTPUT);
  ETS_GPIO_INTR_ENABLE();
}

void loop() {
  ArduinoOTA.handle();
  GPIO_OUT_W1TS = BIT(ESP_BUILTIN_LED);
  delay(1);
  GPIO_OUT_W1TC = BIT(ESP_BUILTIN_LED);
  delay(1);
}
На вывода полная амплитуда
 

pvvx

Активный участник сообщества

pvvx

Активный участник сообщества
#define GPIO_PIN2_CFG(*(volatile uint32_t *)0x60000330)

GPIO_PIN2_CFG &= ~(1<<2); // normal (push-pull)
GPIO_PIN2_CFG |= (1<<2); // open drain
 

Melandr

Member
Это получается нужно в дефайнах любой регистр прописывать, если хочу использовать? Как с AVR не покатит...
 

pvvx

Активный участник сообщества
И оно должно работать и так (*(volatile unsigned int *)0x60000330) &= ~(1<<2); в любом трансляторе C/C++ и любой среде к ESP8266.
 

Melandr

Member
Спасибо за подсказки и помощь. В принципе код без прямого управления через регистры работает, и в принципе мерцание лампы отсутствует. Но при использовании прямого управления почему то ломается обновление прошивки по OTA. Я правильно вставляю запрет и разрешение прерываний?
Код:
//функция инициализации прошивки по "воздуху"
void initializeOTA() {

  //  noInterrupts();
  //  ets_isr_mask(BIT(ETS_GPIO_INUM));
  ETS_GPIO_INTR_DISABLE();

  ArduinoOTA.onStart([]() {
    Serial.println("* OTA: Start");
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\n*OTA: End");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("*OTA: Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("*OTA: Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });

  // Begin
  ArduinoOTA.begin();

  //  interrupts();
  //  ets_isr_unmask(BIT(ETS_GPIO_INUM));
  ETS_GPIO_INTR_ENABLE();
}
Возможно функцию void initializeOTA( ) стоит поставить в начале setup() ?
 

Melandr

Member
А еще вопрос в Arduino IDE есть возможность выбора
2020-12-27_234404.jpg
Что желательно выбрать? Я так понимаю, это тоже может иметь значение при прошивке ESP
 

pvvx

Активный участник сообщества
В принципе код без прямого управления через регистры работает, и в принципе мерцание лампы отсутствует. Но при использовании прямого управления почему то ломается обновление прошивки по OTA. Я правильно вставляю запрет и разрешение прерываний?
ETS_GPIO_INTR_xxx - это по пинам только.
Но ещё есть прерывание таймера.
Только я не пойму, почему OTA мешают какие-то прерывания. Кто так писал? Опять типа iG2019? При обращении к критическому неатомарному коду-функции он должен сам сохранять и восстанавливать регистры разрешений прерываний и прочее....
Возможно функцию void initializeOTA( ) стоит поставить в начале setup() ?
Такие вопросы надо задавать писателям Arduino на их сайте.
А еще вопрос в Arduino IDE есть возможность выбора
Что желательно выбрать? Я так понимаю, это тоже может иметь значение при прошивке ESP
Аналогично - на их сайте должны быть описаны различия версий.
А вам, для игры, наверняка cгодится любая версия. Всё равно дельного из ESP8266 ничего не выйдет.
 

Melandr

Member
pvvx
Спасибки за помощь, после добавления кода включения режима вывода ("push-pull") диммер работает, вот код ниже. Осталось только добавить работу с аппаратным таймером
Код:
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <Ticker.h>
#include "RemoteDebug.h"

#include "ets_sys.h"
#include "gpio.h"

// Instance of RemoteDebug
RemoteDebug Debug;
Ticker blinker;

#define GPIO_OUT_W1TS (*(volatile uint32_t *)0x60000304)
#define GPIO_OUT_W1TC (*(volatile uint32_t *)0x60000308)
#define GPIO_STATUS (*(volatile uint32_t *)0x6000031C)
#define GPIO_STATUS_W1TC (*(volatile uint32_t *)0x60000324)
#define GPIO_PIN2_CFG (*(volatile uint32_t *)0x60000330)

//#define CAYENNE_PRINT Serial    // Comment this out to disable prints and save space
#include <CayenneMQTTESP8266.h>                                     // Include library file for MQTT

#define HOST_NAME "remotedebug"

#define TRIAC_PIN     2         //GPIO2         управление симистором
#define ZC_PIN        1         //GPIO1 - TXD   детектор перехода через "0"
#define BUTTON_PIN    3         //GPIO3 - RXD   кнопка

#define TIME_PULSE 20

void ICACHE_RAM_ATTR zero_crosss_int(void *arg);
void ICACHE_RAM_ATTR onTimerISR();

#ifndef STASSID
#define STASSID "ASUS"
#define STAPSK  "gCU8YNZs"
#endif

const char* ssid = STASSID;
const char* password = STAPSK;

char username[] = "60267f30-21f2-11eb-8779-7d56e82df461";           // Your MQTT cayenne username
char Password[] = "a9631607be6b7979c4c117bbdadc76683b9dbf97";       // Your MQTT cayenne Password
char clientID[] = "bf444650-21fc-11eb-883c-638d8ce4c23d";           // Your MQTT cayenne clientID

volatile int DIMMING_VALUES = 30;
volatile int DIMMING_TIME = 0;
int BUTTON = 0;

byte lightState = 1;           //состояние объекта: 0 - выключен, 1 - включен
int power = 0;
int val = 100;                                       // переменная задающая мощность от 0 до 100

volatile unsigned long countZeroCross = 0;
unsigned long lastMillis = 0;

void setup() {

  initializeOTA();

  Serial.begin(230400);

  gpio_init();
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1);
  PIN_PULLUP_EN(PERIPHS_IO_MUX_U0TXD_U);
  gpio_output_set(0, 0, (1 << TRIAC_PIN), (1 << ZC_PIN));
  GPIO_OUT_W1TC = BIT(TRIAC_PIN);

  //настраиваем пины ESP в зависимости от назначения
  /*  pinMode(TRIAC_PIN, OUTPUT);
    digitalWrite(TRIAC_PIN, LOW);
    pinMode(BUTTON_PIN, INPUT_PULLUP);
    pinMode(ZC_PIN, INPUT_PULLUP);

    attachInterrupt(digitalPinToInterrupt(ZC_PIN), zero_crosss_int, RISING);   // Choose the zero cross interrupt egde selection
  */
  gpio_pin_intr_state_set(ZC_PIN, GPIO_PIN_INTR_POSEDGE);
  ets_isr_attach(ETS_GPIO_INUM, zero_crosss_int, NULL);

  timer1_attachInterrupt(onTimerISR);
  timer1_enable(TIM_DIV16, TIM_EDGE, TIM_SINGLE);

  Cayenne.begin(username, Password, clientID, ssid, password);               // Setup cayenne server for MQTT protocol

  Serial.println("Booting");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(500);
    ESP.restart();
  }

  // Initialize RemoteDebug
  Debug.begin(HOST_NAME); // Initialize the WiFi server
  Debug.setResetCmdEnabled(true); // Enable the reset command
  Debug.showProfiler(true); // Profiler (Good to measure times, to optimize codes)
  Debug.showColors(true); // Colors

  GPIO_PIN2_CFG &= ~(1 << 2); // normal (push-pull)

  // ets_isr_unmask(BIT(ETS_GPIO_INUM));
  ETS_GPIO_INTR_ENABLE();
}

void loop() {

  ArduinoOTA.handle();

  Cayenne.loop();

  if (millis() - lastMillis > 5000) {
    lastMillis = millis();

    debugV("power = %u", power);
    debugV("val = %u", val);
    debugV("countZeroCross = %u", countZeroCross);
    debugV("uptime = %u", millis() / 1000);
    debugV("lihgtState = %u", lightState);
    /*   debugV("countSysTimer = %u", countSysTimer);*/
  }
  //  BUTTON = digitalRead(BUTTON_PIN);

  // RemoteDebug handle
  Debug.handle();
}

//функция инициализаци прошивки по "воздуху"
void initializeOTA() {

  //  noInterrupts();
  //  ets_isr_mask(BIT(ETS_GPIO_INUM));
  ETS_GPIO_INTR_DISABLE();

  ArduinoOTA.onStart([]() {
    Serial.println("* OTA: Start");
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\n*OTA: End");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("*OTA: Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("*OTA: Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });

  // Begin
  ArduinoOTA.begin();

  //  interrupts();
  //  ets_isr_unmask(BIT(ETS_GPIO_INUM));
  ETS_GPIO_INTR_ENABLE();
}

void ICACHE_RAM_ATTR zero_crosss_int(void *arg)                   //функция, которая запускается при пересечении нуля, чтобы изменить яркость света
{
  (void)arg;
  uint32_t tmp = GPIO_STATUS;
  GPIO_STATUS_W1TC = tmp;
  if (tmp & BIT(ZC_PIN))
  {
    power = 49500 - 490 * val;
    timer1_write(power);
    countZeroCross++;
  }
}

void ICACHE_RAM_ATTR onTimerISR()  // обработчик прерывания таймера
{

  if (lightState == 1)
  {
    //    digitalWrite(TRIAC_PIN, HIGH);
    GPIO_OUT_W1TS = BIT(TRIAC_PIN);
    delayMicroseconds(TIME_PULSE);
    //    digitalWrite(TRIAC_PIN, LOW);
    GPIO_OUT_W1TC = BIT(TRIAC_PIN);

    timer1_write(50000);//10мс при тике - 0,2 мкс
  }
}

CAYENNE_OUT_DEFAULT()
{
  // Запишите данные в Cayenne здесь. В этом примере просто отправляется текущее время безотказной работы в миллисекундах на виртуальном канале 0.
  Cayenne.virtualWrite(V0, millis() / 1000);
  Cayenne.virtualWrite(V1, BUTTON);
  Cayenne.virtualWrite(V2, countZeroCross);
}

CAYENNE_IN(3)
{
  int Dimm_Val = getValue.asInt();
  val = Dimm_Val;
}

CAYENNE_IN(4)
{
  lightState = getValue.asInt();
  if (lightState == 1)
  {
    //    digitalWrite(TRIAC_PIN, 1);  // to get the value from the website
    GPIO_OUT_W1TS = BIT(TRIAC_PIN);
  }
  else
  {
    //    digitalWrite(TRIAC_PIN, 0);  // to get the value from the website
    GPIO_OUT_W1TC = BIT(TRIAC_PIN);
  }
}
Конечно джиттер присутствует ы импульсе управления, посмотрим, что получится с аппаратным таймером
 

Melandr

Member
Добрый день, не подскажите, разбираю я код обработчиков прерываний и не могу понять
Код:
void ICACHE_RAM_ATTR zero_crosss_int(void *arg)                   //функция, которая запускается при пересечении нуля, чтобы изменить яркость света
{
  (void)arg;
  uint32_t tmp = GPIO_STATUS;
  GPIO_STATUS_W1TC = tmp;
  if (tmp & BIT(ZC_PIN))
  {
    power = 49500 - 490 * val;
    timer1_write(power);
    countZeroCross++;
  }
}

void ICACHE_RAM_ATTR onTimerISR()  // обработчик прерывания таймера
{

  if (lightState == 1)
  {
    //    digitalWrite(TRIAC_PIN, HIGH);
    GPIO_OUT_W1TS = BIT(TRIAC_PIN);
    delayMicroseconds(TIME_PULSE);
    //    digitalWrite(TRIAC_PIN, LOW);
    GPIO_OUT_W1TC = BIT(TRIAC_PIN);

    timer1_write(50000);//10мс при тике - 0,2 мкс
  }
}
Для чего в обработчике void ICACHE_RAM_ATTR onTimerISR() вызывается таймер1 со значением 50000?
Если я правильно понимаю логику работы диммера, то мы должны в обработчике перехода через "0" - void ICACHE_RAM_ATTR zero_crosss_int(void *arg) занести в таймер значение задержки управляющего импульса симистора в зависимости от требуемой яркости. И далее после срабатывания прерывания таймера1 в обработчике данного прерывания сформировать импульс управления и далее ожидать следующего перехода через "0". Зачем в этом обработчике заносится в таймер значение 50000?
 
Сверху Снизу