• Система автоматизации с открытым исходным кодом на базе 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?
 
Сверху Снизу