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

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

Melandr

Member
Попробовал дергать пином при помощи функции gpio SDK, код ниже
Код:
#include "gpio.h"
#include "user_interface.h"
#include "hw_timer.h"

#define TIME_PULSE 50
#define MAX_DIMMING_VALUE 255
#define MIN_DIMMING_VALUE 0

#define PWM_PIN     2         //GPIO2         

byte tickOccured;
int state = 0;
int period = 10000;
int dimDelay = 0;
int timePause;
volatile byte tarBrightness = 20;   //Желаемая яркость 1-100
volatile byte curBrightness = 20;     //Текущая яркость
unsigned long lastMillis = 0;

void ICACHE_RAM_ATTR hw_test_timer_cb() {

  hw_timer_init(NMI_SOURCE, 0);
  curBrightness = tarBrightness;

  if (state == 0) {
    gpio_output_set(0, (1 << PWM_PIN), 0, 0);
    state = 1;
    hw_timer_arm(TIME_PULSE);
    tickOccured = 0;
  }
  else if (state == 1) {
    // set gpio high
    gpio_output_set((1 << PWM_PIN), 0, 0, 0);
    state = 2;
    hw_timer_arm(timePause);
    tickOccured = 1;
  }
  else if (state == 2) {
    gpio_output_set(0, (1 << PWM_PIN), 0, 0);
    state = 0;
    hw_timer_arm(dimDelay);
    tickOccured = 2;
  }
}

void ICACHE_FLASH_ATTR user_init(void) {

  // init gpio subsytem
  gpio_init();
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
  // set gpio low
  gpio_output_set(0, (1 << PWM_PIN), 0, 0);

  hw_timer_init(NMI_SOURCE, 0);
  //hw_timer_init(FRC1_SOURCE, 0);
  hw_timer_set_func(hw_test_timer_cb);
  hw_timer_arm(dimDelay);
} // End of user_init

void setup() {

  Serial.begin(115200);
  Serial.println();
  Serial.println();

  Serial.println("");
  Serial.println("--------------------------");
  Serial.println("ESP8266 Timer Test");
  Serial.println("--------------------------");

  dimDelay = 39.1 * (MAX_DIMMING_VALUE - curBrightness);
  timePause = period - (dimDelay + TIME_PULSE);

  user_init();

}

void loop() {
  /*
    if (tickOccured == true)
    {
      Serial.println("Tick Occurred = ");
      Serial.println(tickOccured);
    }*/
  dimDelay = 39.1 * (MAX_DIMMING_VALUE - curBrightness);
  timePause = period - (dimDelay + TIME_PULSE);

  if (millis() - lastMillis > 5000) {
    lastMillis = millis();
    Serial.print("dimDelay = ");
    Serial.println(dimDelay);
    Serial.print("timePause = ");
    Serial.println(timePause);
  }

  yield();  // or delay(0);

  system_soft_wdt_feed();
} //end loop
Скетч компилируется, но смотрю осциллографом и не вижу импульсов. При этом если использую функции управления пинами Arduino IDE, то импульсы есть, но при указании импульса 50 мкс - по факту выходит импульс 1,16 мс.
Может неправильно инициализирую вывод в формате SDK?
Если управление выводом в формате Arduino, то все работает, типа, код ниже
Код:
#include "user_interface.h"
#include "hw_timer.h"

#define TIME_PULSE 50
#define MAX_DIMMING_VALUE 255
#define MIN_DIMMING_VALUE 0

#define PWM_PIN     2         //GPIO2         

byte tickOccured;
int state = 0;
int period = 10000;
int dimDelay = 0;
int timePause;
volatile byte tarBrightness = 30;   //Желаемая яркость 1-100
volatile byte curBrightness = 30;     //Текущая яркость
unsigned long lastMillis = 0;

void ICACHE_RAM_ATTR hw_test_timer_cb() {

  hw_timer_init(NMI_SOURCE, 0);
  curBrightness = tarBrightness;

  if (state == 0) {
    digitalWrite(PWM_PIN, LOW);
    state = 1;
    hw_timer_arm(TIME_PULSE);
    tickOccured = 0;
  }
  else if (state == 1) {
    digitalWrite(PWM_PIN, HIGH);
    state = 2;
    hw_timer_arm(timePause);
    tickOccured = 1;
  }
  else if (state == 2) {
    digitalWrite(PWM_PIN, LOW);
    state = 0;
    hw_timer_arm(dimDelay);
    tickOccured = 2;
  }
}

void ICACHE_FLASH_ATTR user_init(void) {

  hw_timer_init(NMI_SOURCE, 0);
  //hw_timer_init(FRC1_SOURCE, 1);
  hw_timer_set_func(hw_test_timer_cb);
  hw_timer_arm(dimDelay);
} // End of user_init

void setup() {

  Serial.begin(115200);
  Serial.println();
  Serial.println();

  Serial.println("");
  Serial.println("--------------------------");
  Serial.println("ESP8266 Timer Test");
  Serial.println("--------------------------");

  pinMode(PWM_PIN, OUTPUT);
  digitalWrite(PWM_PIN, LOW);

  dimDelay = 39.1 * (MAX_DIMMING_VALUE - curBrightness);
  timePause = period - (dimDelay + TIME_PULSE);

  user_init();

}

void loop() {
  /*
    if (tickOccured == true)
    {
      Serial.println("Tick Occurred = ");
      Serial.println(tickOccured);
    }*/
  dimDelay = 39.1 * (MAX_DIMMING_VALUE - curBrightness);
  timePause = period - (dimDelay + TIME_PULSE);

  if (millis() - lastMillis > 5000) {
    lastMillis = millis();
    Serial.print("dimDelay = ");
    Serial.println(dimDelay);
    Serial.print("timePause = ");
    Serial.println(timePause);
  }
  yield();  // or delay(0);

  system_soft_wdt_feed();
} //end loop
 

pvvx

Активный участник сообщества
А почему digitalWrite(N_pin,..) должен совпадать с номерами пинов в gpio_output_set()?
И почему: system_soft_wdt_feed(); а не ESP.wdtFeed() ?
И yield() - разве его нет в процедуре вызова цикла loop()?
 

pvvx

Активный участник сообщества
Но учтите, что GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1<<15); использует 2 такта устройства GPIO. GPIO тактируется от кварца. (прямая запись в регистр значения вывода gpio даст 1 такт)
Так-же компилятор вставляет команду MEMW() при обращение к volatile, т.е. команду сброса кеша проца, которого у него нет :)
И так-же, если шина от проца к GPIO устройству занята (забито её fifo), то CPU будет остановлен на несколько тактов этой узкой шины, тактируемой на частоте кварца.
И так-же, если коды команд данного участка памяти были вытеснены из кеша Flash, то скорость выполнения притормозит на длительность времени последовательного считывания нескольких байт из Flash...
Т.е. джиттер вы всё равно получите.
 

nikolz

Well-known member
Доброе утро!
А не подскажите, где посмотреть соответствие номеров пинов для SDK применительно к ESP-01.
номера пинов для SDK - это пины ESP8266. для ESP-01 - на схеме соединение ESP8266 на внешние пины ESP-01.
---------------
информация к размышлению:
----------------------
У вас в ESP уже работает системный таймер который гоняет пустой бесконечный цикл Loop.
вы ему параллельно запускаете еще один таймер.
Т е уже два таймера жрут энергию и ничего не делают.
при цикличном запуске второго таймера у вас в колбеке параллельно с двумя таймерами кушает еще и CPU.
если будете одноразово запускать второй таймер, то при работе CPU он будет отдыхать.
Но логично спросить , возможно не стоит греть микросхему и отказаться от второго таймера?
---------------
Если есть роутер, то нет смысла запускать ESP как AP.
 

nikolz

Well-known member
если выкинуть второй таймер
то вариант скетча для диммера будет примерно такой:
...
void setup()
{
TRIAC_Out(); //Настраиваем порт управления на выход
ZERO_In(); //настраиваем порт на вход для отслеживания прохождения сигнала через ноль
attachInterrupt(0, detect,High); // настроить срабатывание прерывания interrupt0 от pin на высокий уровень
}
void loop()
{ TRIAC_Low();
if ((Dimmer--)==0) TRIAC_High();
}
//*******обработчики прерываний*
void detect()
{ Dimmer=Delta; } //записываем величину задержки нормированную к длительности цикла loop
 

pvvx

Активный участник сообщества
Но логично спросить , возможно не стоит греть микросхему и отказаться от второго таймера?
От таймера "микросхема" не греется. Это ничтожные доли процента от потребления RF части или самого CPU.
И без разницы включен на счет таймер или нет, т.к. CLK на него уже включено.
 

pvvx

Активный участник сообщества
Сеть я анализирую, есть детектор перехода через "0".
А надо ещё и огибающую для выделения или порции по энергии или по напряжению (зависит что вы диммируете), чтобы знать искажение текущего куска синусоиды (отдаваемую порцию) и защелкивающимся тиристором это не решается.
 

Melandr

Member
{ Dimmer=Delta; } //записываем величину задержки нормированную к длительности цикла loop
Вот такая запись меня и смущает. Допустим сейчас у меня в основном цикле ничего нет. И я подберу длительность Delta. Далее я решил добавить функционал и подключил 4 датчика Ds1820, у меня изменится длительность loop? Значит опять нужно подбирать значение переменной Delta?
ЗЫ: Делал диммер на AVR с частотой 16 МГц, и никаких проблем. Здесь имеем проц 80 или 160 МГц, и не получается сделать импульс 20 мкс
 

pvvx

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

pvvx

Активный участник сообщества
ЗЫ: Делал диммер на AVR с частотой 16 МГц, и никаких проблем. Здесь имеем проц 80 или 160 МГц, и не получается сделать импульс 20 мкс
Отключите WiFi в ESP8266 и всё будет аналогично AVR, кроме наличия даташита на ESP8266.
И решение построения самого диммера на AVR для вас более правильное, чем использование ESP8266.
 

pvvx

Активный участник сообщества
Melandr Вы столкнулись с проприетарным закрытым ПО и чипом.

Это выгодно некоторым – на этом они строят свой мелочный бизнес.

Без этого и данный форум не смог бы существовать. Описаний к чипу ESP8266 и прочим его побрякушкам нет и не предвидится. Вот если вы разберетесь хотя-бы с таймером, то будете ли писать и поддерживать какую-то статью для всех? Форум не имеет таких средств, как создание каталогов. И каждый, уже в течении многих лет спрашивает одно и то-же… И каждому из них надо описать на том языке, на котором он поймет (т.е. разобрав его уровень познаний)… На этом и держится популярность ESP8266.
 

Melandr

Member
По этому Ардуинщики имеют только гадания на кофейной гуще.
Скажу честно, Arduino IDE подкупает наличием большого количества библиотек и примеров, которых ощутимо меньше, чем для AVR и в разы меньше, чем для ESP
А надо ещё и огибающую для выделения или порции по энергии или по напряжению (зависит что вы диммируете), чтобы знать искажение текущего куска синусоиды (отдаваемую порцию) и защелкивающимся тиристором это не решается.
Ну настолько сильно я пока не заморачиваюсь, на лампочке по-идее ток и напряжения должны совпадать, а на индуктивной нагрузке я и хочу делать импульс управления не слишком длинным, чтобы не было импульса открытия симистора пока через него течет ток.
Для Arduino они задаются в каталоге /variants для каждой платы в файле pins_arduino.h.
Пытаюсь найти по ESP8266 (ESP-01) соответствие, но такого файла нет.
А еще вопрос по GPIO
нашел такие макросы
Код:
#define GPO    ESP8266_REG(0x300) //GPIO_OUT R/W (Output Level)
#define GPOS   ESP8266_REG(0x304) //GPIO_OUT_SET WO
#define GPOC   ESP8266_REG(0x308) //GPIO_OUT_CLR WO
#define GPOP(p) ((GPO & (1 << ((p) & 0xF))) != 0)
еще есть так
Код:
    gpio_init();
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13);
    GPIO_OUTPUT_SET(13, 0);
и вот так
Код:
  // init gpio subsytem
  gpio_init();

  // configure UART TXD to be GPIO1, set as output
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1);
  gpio_output_set(0, 0, (1 << pin), 0);
Применительно к Arduino IDE, какой синтаксис можно использовать?
ЗЫ: Также попадалось где-то на форумах, что функции WiFi крутятся на таймере 0, и поэтому доступен для использования таймер 1. Почему тогда проблема его использовать, или таймер 1 имеет меньший приоритет, по сравнению с таймер 0?
 

Melandr

Member
Но учтите, что GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1<<15); использует 2 такта устройства GPIO. GPIO тактируется от кварца. (прямая запись в регистр значения вывода gpio даст 1 такт)
Так-же компилятор вставляет команду MEMW() при обращение к volatile, т.е. команду сброса кеша проца, которого у него нет :)
И так-же, если шина от проца к GPIO устройству занята (забито её fifo), то CPU будет остановлен на несколько тактов этой узкой шины, тактируемой на частоте кварца.
И так-же, если коды команд данного участка памяти были вытеснены из кеша Flash, то скорость выполнения притормозит на длительность времени последовательного считывания нескольких байт из Flash...
Т.е. джиттер вы всё равно получите.
Спасибо, интересная информация
 

pvvx

Активный участник сообщества
еще есть так
Код:
    gpio_init();
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13);
    GPIO_OUTPUT_SET(13, 0);
Это глупость. Зачем инициализировать систему функций в ROM для GPIO, а далее лезть непосредственно в регистры?
и вот так
Код:
  // init gpio subsytem
  gpio_init();

  // configure UART TXD to be GPIO1, set as output
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1);
  gpio_output_set(0, 0, (1 << pin), 0);
Применительно к Arduino IDE, какой синтаксис можно использовать?
Считается что нельзя. Типа это обращения через функции в ROM чипа.

ЗЫ: Также попадалось где-то на форумах, что функции WiFi крутятся на таймере 0, и поэтому доступен для использования таймер 1. Почему тогда проблема его использовать, или таймер 1 имеет меньший приоритет, по сравнению с таймер 0?
Бред и ещё раз бред.
Приоритетов у таймеров нет. Просто таймер 0 используется в некоторых функциях. Описания в каких нет и не будет - Arduino же :p
 

pvvx

Активный участник сообщества
В Arduino своя система работы с GPIO. Самая извращенная. Номера выводов имеют привязку к краске на плате около контактов.
 

Melandr

Member
посмотрел в этой теме Ускорение esp8266 работу с GPIO и не могу понять, почему у товарища CodeNameHawk
функция digitalWrite занимает 0,46 мкс. А у меня тот же ногодрыг с использованием прерывания от таймера 1 забирает на полтора порядка больше времени.
 

Melandr

Member
Почитал перевод FAQ по ESP8266 http://microsin.net/programming/arm/espressifcom-faq-esp8266.html
Алгоритм работы с таймером 1
Опорной для 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 мкс.

Непонятен алгоритм смены значения регистра FRC1_LOAD_VALUE в прерывании. Также если отключен auto-feed-mode в регистр заносится максимальное значение и счет продолжается. А когда же нужно заносить в регистр новое значение?
 
Сверху Снизу