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

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

pvvx

Активный участник сообщества
Во как бомбануло! Говорили мне не трогай ка*у, вонять не будет. Не послушался. Еще и защитнечков притащил ...
Что вас так колбасит... не слушаетесь и защитников пригласили?
Любителей загребать жар чужими руками издалека видно
Решили гореть вместе?
Вас уже занести в анналы - гордиться надо. :)
 

pvvx

Активный участник сообщества
Вы знаете, PlatformIO понравился, но допустим, я создаю новый проект. Выбираю плату - ESP8266 generic . Но на этой плате перепаяна микросхема памяти с 512 кБ, на 4 МБ. А в списке доступны только 512 кБ и 1 МБ.
Вам опция "1 МБ" точно сгодится, т.к. остальную область из 4 МБ вы пока не используете, да и кэш чипа к SPI-Flash ограничена единовременным доступом в "1 МБ". Остальное пространство обычно используют для хранения данных...
Не подскажите, как его добавить в PlatformIO? При создании проекта выбрать фреймворк другой? Для этого нужно с сайта Espressif скачать SDK NONOS и установить в систему?
Не смогу подсказать - не использую PlatformIO. Смотрел её несколько лет назад, а ныне оно разрослось и все опции там другие...
Не использую по простой причине - оно основано на проприетарном ПО. В основе VS - закрытое ПО. Нет возможности коммерческого использования без покупки разнообразных лицензий.
Для windows минимум лицензий требуется в случае использования чего-то типа Eclipse - в такой связке нужна покупка только самой системы windows.
 

Melandr

Member
pvvx, добрый день!
А не подскажите еще момент. При объявлении обработчиков прерываний, Вы указываете параметром void *arg
Код:
void ICACHE_RAM_ATTR GPIOs_intr_handler(void *arg) {
  (void)arg;
  uint32_t tmp = GPIO_STATUS;
  GPIO_STATUS_W1TC = tmp;
  if (tmp & BIT(GPIO_IN)) {
    TIMER_LOAD = pulse1_in_0us2;
    TIMER_CTRL = 4 | BIT(7); // TM_DIVDED_BY_16, NO TM_AUTO_RELOAD_CNT, TM_ENABLE_TIMER
  }
}

void ICACHE_RAM_ATTR TIMER_intr_cb(void *arg) {
  (void)arg;
  if (GPIO_OUTP & BIT(GPIO_OUT)) {
    GPIO_OUT_W1TC = BIT(GPIO_OUT);
    TIMER_CTRL = 0; // stop timer
    gpio_pin_intr_state_set(GPIO_IN, GPIO_PIN_INTR_POSEDGE);
  } else {
    GPIO_OUT_W1TS = BIT(GPIO_OUT);
    TIMER_LOAD = pulse2_in_0us2;
  }
}
Если использовать библиотеку hw_timer, то при указании функции обработчика прерывания выдает ошибку
error: invalid conversion from 'void (*)(void*)' to 'void (*)()' [-fpermissive]

Код:
static void (* user_hw_timer_cb)(void) = NULL;
/******************************************************************************
  FunctionName : hw_timer_set_func
  Description  : set the func, when trigger timer is up.
  Parameters   : void (* user_hw_timer_cb_set)(void):
                        timer callback function,
  Returns      : NONE
*******************************************************************************/
void  hw_timer_set_func(void (* user_hw_timer_cb_set)(void))
{
  user_hw_timer_cb = user_hw_timer_cb_set;
}

static void  hw_timer_isr_cb(void)
{
  if (user_hw_timer_cb != NULL) {
    (*(user_hw_timer_cb))();
  }
}
Если убрать void *arg из параметров обработчика и объявление (void)arg;, тогда компилируется нормально
 

pvvx

Активный участник сообщества
Если использовать библиотеку hw_timer . . .
Я не знаю библиотек hw_timer и какие там описания функций.
Оно может вообще не по прерыванию вызывать ваш callback().
А (void) x; указывает компилятору, что x не используется. Иначе будет warning. Но обычно в дурине не включен режим нормальной проверки warning...
 

pvvx

Активный участник сообщества
Дайте опцию компилятору в Arduino ESP8266 в файле platform.txt
compiler.warning_flags.all=-Wall -Wextra -pedantic
и на любом примере получите простыню warning на более 3 мегабайта.
Дети же в детсаде все либы писали :) Синтаксис Си они не изучали и лепили всё по наитию и путем "работаЭт" или "неработаЭт" :)
 

Melandr

Member
pvvx, доброй ночи!
Не могу понять, в чем прикол.
Если для остановки таймера использую выражение
TIMER_CTRL = 0; // stop timer
Все нормально.
Если же использую функцию
Код:
void hw_timer_disarm(void)
{
  RTC_REG_WRITE(FRC1_CTRL_ADDRESS, 0);
}
Эта функция может вызываться и в прерывании. То тогда ESP уходит в перезагрузку.
Если же я просто вместо
TIMER_CTRL = 0; // stop timer
использую
RTC_REG_WRITE(FRC1_CTRL_ADDRESS, 0);
то тоже все работает.
Почему не работает при использовании функции?
 

pvvx

Активный участник сообщества
Потому, что функция находится в Flash, а не в IRAM
ICACHE_RAM_ATTR void hw_timer_disarm(void) {
RTC_REG_WRITE(FRC1_CTRL_ADDRESS, 0);
}
И зачем вам лишний код? Для увеличения его размера?
 

Melandr

Member
Добавлял атрибут ICACHE_RAM_ATTR всем функциям в библиотеке
hw_timer
Код:
/******************************************************************************
  Copyright 2013-2014 Espressif Systems (Wuxi)

  FileName: hw_timer.c

  Description: hw_timer driver

  Modification history:
      2014/5/1, v1.0 create this file.
*******************************************************************************/
//#include "c-types.h"
typedef __SIZE_TYPE__ size_t;
#include "ets_sys.h"
#include "os_type.h"
#include "osapi.h"
#include "hw_timer.h"

/******************************************************************************
    @brief   disable this timer.

    @param   null

    @return  null
  ****************************************************************************/
void hw_timer_disarm(void)
{
  ETS_FRC1_INTR_DISABLE();
  TM1_EDGE_INT_DISABLE();
  RTC_REG_WRITE(FRC1_CTRL_ADDRESS, 0);
}
/******************************************************************************
  FunctionName : hw_timer_arm
  Description  : set a trigger timer delay for this timer.
  Parameters   : uint32 val :
  in autoload mode
                        50 ~ 0x7fffff;  for FRC1 source.
                        100 ~ 0x7fffff;  for NMI source.
  in non autoload mode:
                        10 ~ 0x7fffff;
  Returns      : NONE
*******************************************************************************/
void hw_timer_arm(u32 val)
{
  RTC_REG_WRITE(FRC1_LOAD_ADDRESS, US_TO_RTC_TIMER_TICKS(val));
}

static void (* user_hw_timer_cb)(void) = NULL;
/******************************************************************************
  FunctionName : hw_timer_set_func
  Description  : set the func, when trigger timer is up.
  Parameters   : void (* user_hw_timer_cb_set)(void):
                        timer callback function,
  Returns      : NONE
*******************************************************************************/
void  hw_timer_set_func(void (* user_hw_timer_cb_set)(void))
{
  user_hw_timer_cb = user_hw_timer_cb_set;
}

static void  hw_timer_isr_cb(void)
{
  if (user_hw_timer_cb != NULL) {
    (*(user_hw_timer_cb))();
  }
}

/******************************************************************************
  FunctionName : hw_timer_init
  Description  : initilize the hardware isr timer
  Parameters   :
  FRC1_TIMER_SOURCE_TYPE source_type:
                        FRC1_SOURCE,    timer use frc1 isr as isr source.
                        NMI_SOURCE,     timer use nmi isr as isr source.
  u8 req:
                        0,  not autoload,
                        1,  autoload mode,
  Returns      : NONE
*******************************************************************************/
void ICACHE_FLASH_ATTR hw_timer_init(FRC1_TIMER_SOURCE_TYPE source_type, u8 req)
{
  if (req == 1) {
    RTC_REG_WRITE(FRC1_CTRL_ADDRESS,
                  FRC1_AUTO_LOAD | DIVDED_BY_16 | FRC1_ENABLE_TIMER | TM_EDGE_INT);
  } else {
    RTC_REG_WRITE(FRC1_CTRL_ADDRESS,
                  DIVDED_BY_16 | FRC1_ENABLE_TIMER | TM_EDGE_INT);
  }

  if (source_type == NMI_SOURCE) {
    ETS_FRC_TIMER1_NMI_INTR_ATTACH(hw_timer_isr_cb);
  } else {
    ETS_FRC_TIMER1_INTR_ATTACH(hw_timer_isr_cb, NULL);
  }

  TM1_EDGE_INT_ENABLE();
  ETS_FRC1_INTR_ENABLE();
}

//-------------------------------Test Code Below--------------------------------------
#if 0
void   hw_test_timer_cb(void)
{
  static uint16 j = 0;
  j++;

  if ((WDEV_NOW() - tick_now2) >= 1000000) {
    static u32 idx = 1;
    tick_now2 = WDEV_NOW();
    os_printf("b%u:%d\n", idx++, j);
    j = 0;
  }

  //hw_timer_arm(50);
}

void ICACHE_RAM_ATTR user_init(void)
{
  hw_timer_init(FRC1_SOURCE, 1);
  hw_timer_set_func(hw_test_timer_cb);
  hw_timer_arm(100);
}
#endif
/*
  NOTE:
  1 if use nmi source, for autoload timer , the timer setting val can't be less than 100.
  2 if use nmi source, this timer has highest priority, can interrupt other isr.
  3 if use frc1 source, this timer can't interrupt other isr.

*/
Тоже мысль возникла, что функции вызываются из FLASH, а нужно из RAM. Добавил атрибут ICACHE_RAM_ATTR
Возможно функции следует сделать встраиваемыми?
И зачем вам лишний код? Для увеличения его размера?
Просто неспешно разбираюсь с ESP8266 и захотелось понять, почему Ваш код работает, а аналогичный код нет. Ну и когда код сделан не одним полотном, а разбит на модули проще воспринимается.
 

pvvx

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

pvvx

Активный участник сообщества
Если вам нравится многословие, то пишите так в хидере:
inline void hw_timer_disarm(void) {
RTC_REG_WRITE(FRC1_CTRL_ADDRESS, 0);
}
 

pvvx

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

pvvx

Активный участник сообщества
И куча непонятных названий функций приведет к полностью нечитаемому коду, без изучения ваших личных предрассудков в специальном талмуде описания библиотек.
Так и получается в Arduino - никому не нужные функции пытающиеся сделать всё за тупого пользователя и тем самым полностью ограничивающие варианты алгоритмов реализации, годящиеся только для копирования всего кода приложения без возможности какой либо модификации.
Вы уже этим и занялись - исправляете чужую кривую либу "hw_timer"... Получится ещё один специфический вариант "hw_timer" :)
 

Melandr

Member
pvvx, добрый вечер!
Хотел задать Вам еще пару вопросов. Надеюсь еще не сильно Вас достал.
Вопрос 1: В обработчике внешнего прерывания по входу
Код:
void ICACHE_RAM_ATTR GPIOs_intr_handler(void *arg) {
  (void)arg;
  uint32_t tmp = GPIO_STATUS;
  GPIO_STATUS_W1TC = tmp;
  if (tmp & BIT(GPIO_IN)) {
    TIMER_LOAD = pulse1_in_0us2;
    TIMER_CTRL = 4 | BIT(7); // TM_DIVDED_BY_16, NO TM_AUTO_RELOAD_CNT, TM_ENABLE_TIMER
  }
  countZeroCross++;
}
строка uint32_t tmp = GPIO_STATUS; в переменную заносим регистр GPIO_STATUS
Далее в регистр GPIO_STATUS_W1TC копируем переменную tmp
Далее проверяем, установлен ли в переменной tmp бит, соответствующий пину GPIO_IN. Для чего нужны были эти три операции
Вопрос 2. Разобравшись немного с примером диммера, который Вы сделали выше по теме, я задумался, как используя представленный Вами код сделать управление двумя симисторами. Но с одним таймером как-то не получалось. Далее на просторах интернета был найден пример двухканального диммера, с использованием одного программного таймера. Я переделал код этого диммера для использования аппаратного таймера ESP. Код ниже
Код:
#include "ets_sys.h"
#include "gpio.h"

#define INTC_EDGE_EN  (*(volatile uint32_t *)0x3FF00004)
#define TIMER_LOAD (*(volatile uint32_t *)0x60000600)
#define TIMER_COUNT (*(volatile uint32_t *)0x60000604)
#define TIMER_CTRL (*(volatile uint32_t *)0x60000608)
#define TIMER_INT  (*(volatile uint32_t *)0x6000060c)
#define GPIO_OUT_W1TS (*(volatile uint32_t *)0x60000304)
#define GPIO_OUT_W1TC (*(volatile uint32_t *)0x60000308)
#define GPIO_OUTP (*(volatile uint32_t *)0x60000300)
#define GPIO_STATUS (*(volatile uint32_t *)0x6000031C)
#define GPIO_PIN2_CFG (*(volatile uint32_t *)0x60000330)
#define GPIO_STATUS_W1TC (*(volatile uint32_t *)0x60000324)

/*Relay no*/
static const int DIMMABLE_TRIAC_1 = 2; // GPIO2

/*ZCD*/
static const int ZCD_IN = 1; // GPIO1
static const int Dimmer_width = 100;

/*ZCD Variables*/
int freqStep = 98;//10 мс
int Dimmer_value_one = 20;
int Dimmer_value_two = 20;
volatile int dim_value_one = 0;
volatile int dim_value_two = 0;
int dimming_one = 0;
int dimming_two = 0;
volatile boolean zero_cross_one = 0;
volatile boolean zero_cross_two = 0;
volatile int countTimer = 0;

void ICACHE_RAM_ATTR zero_cross_detect(void *arg) {
  (void)arg;
  uint32_t tmp = GPIO_STATUS;
  GPIO_STATUS_W1TC = tmp;
  if (tmp & BIT(ZCD_IN)) {
    zero_cross_one = true;               // set the boolean to true to tell our dimming function that a zero cross has occured
    dim_value_one = 0;
    GPIO_OUT_W1TC = BIT(DIMMABLE_TRIAC_1);      // turn off TRIAC (and AC)

    TIMER_LOAD = freqStep * 5;
    TIMER_CTRL = 4 // TM_DIVDED_BY_16
                 | BIT(6) // TM_AUTO_RELOAD_CNT
                 | BIT(7) //  TM_ENABLE_TIMER
                 ;
  }
}

void ICACHE_RAM_ATTR dim_check(void *arg) {
  (void)arg;
  countTimer++;
  /*For Dimmer CHANELL_1*/
  if (zero_cross_one == true)
  {
    if (dim_value_one >= dimming_one)
    {
      GPIO_OUT_W1TS = BIT(DIMMABLE_TRIAC_1); // turn on Triac

      dim_value_one = 0; // reset time step counter
      zero_cross_one = false; //reset zero cross detection

    }
    else
    {
      dim_value_one++; // increment time step counter
    }
  }

  if (countTimer >= Dimmer_width) {
    TIMER_CTRL = 0; // stop timer
    gpio_pin_intr_state_set(ZCD_IN, GPIO_PIN_INTR_NEGEDGE);
    countTimer = 0;
  }
}

void dimmer_stop() {
  ets_isr_mask(BIT(ETS_FRC_TIMER1_INUM) | BIT(ETS_GPIO_INUM)); // запретить прерывания GPIOs & Timer0
  GPIO_OUT_W1TC = BIT(DIMMABLE_TRIAC_1);
  TIMER_CTRL = 0; // stop timer
}

void dimmer_start() {
  dimmer_stop();
  TIMER_COUNT = 0;
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1);
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
  gpio_output_set(0, 0, BIT(DIMMABLE_TRIAC_1), BIT(ZCD_IN));
  GPIO_PIN2_CFG &= ~BIT(DIMMABLE_TRIAC_1); // normal out (push-pull)
  gpio_pin_intr_state_set(ZCD_IN, GPIO_PIN_INTR_POSEDGE);
  ets_isr_attach(ETS_GPIO_INUM, zero_cross_detect, NULL);
  ets_isr_attach(ETS_FRC_TIMER1_INUM, dim_check, NULL);
  INTC_EDGE_EN |= BIT(1); // + timer0

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

void setup() {
  gpio_init();
  dimmer_start();
}

void loop() {

  dimming_one = Dimmer_width - Dimmer_value_one;
}
Но при использовании данного алгоритма, иногда проскальзывают редкие мерцания, но не могу понять, с чем они могут быть связаны. При использовании Вашего кода таких мерцаний нет. Не подскажите, что может проблема? Или может направите, как можно переделать Ваш код для управления двумя каналами симисторов.
 

pvvx

Активный участник сообщества
GPIO_STATUS имеет атрибут volatile, а так-же, за время между командами может произойти что угодно - пусть пачка NMI прерываний... А мы отрабатываем то, что было, а что будет новое - это следующим разом, чтобы не потерять смены состояний.
И подтверждаем (сбрасываем) только те флаги в GPIO_STATUS_W1TC, что были и что обрабатываем. Новые вызовут новое прерывание... и это будет новое событие, а не профуканое.
 

Melandr

Member
pvvx, а еще вопрос, а есть ли в ESP такие же возможности по настройке таймера как в AVR - регистры OCR1A и OCR1B, регистр TCNT1, прерывание по переполнению или по совпадению. или есть только Timer0, который считает от TIMER_LOAD и до 0. И прерывание, которое срабатывает при 0.
 

Melandr

Member
INTC_EDGE_EN (0x3FF00004)
Controls which peripheral interrupts are actually passed through to the CPU by the interrupt controller.
Управляет тем, какие периферийные прерывания фактически передаются в ЦП контроллером прерываний.

Не могу понять, что подразумевается. Этот регистр выбирает, какие прерывания обрабатывать? Одновременно нельзя обрабатывать прерывания от собаки и таймера0?
 

pvvx

Активный участник сообщества
INTC_EDGE_EN (0x3FF00004)
Controls which peripheral interrupts are actually passed through to the CPU by the interrupt controller.
Управляет тем, какие периферийные прерывания фактически передаются в ЦП контроллером прерываний.

Не могу понять, что подразумевается. Этот регистр выбирает, какие прерывания обрабатывать? Одновременно нельзя обрабатывать прерывания от собаки и таймера0?
В ESP8266 всё зашифровано. Вы наблюдаете только ту информацию, что вынудили выдать Espressif так называемым "обратным инжинирингом" и предоставлением в открытый доступ. И то не всё открытое и описанное другими Espressif выложило в отк.доступ или просто подтвердили. Это типа, что ESP8266 сделан не в Espressif и она ничего по нему толком не знает. На следующие чипы ESP32 им дали частичное описание и они ими барыжат :)
 

Melandr

Member
pvvx, доброй день!
Хотел с Вами посоветоваться. Использую для отслеживания перехода через 0 спад волны полупериода сетевого напряжения.
gpio_pin_intr_state_set(ZCD_IN, GPIO_PIN_INTR_NEGEDGE);
Далее в обработчике прерывания выключаю выход на симистор. И на осциллографе вижу, что до самого перехода через 0 по времени не дотягивает еще 500 мкс. Можно ли как-то скорректировать неточность детектора перехода через 0 и учесть в программе? Или смысла в этом нет?
Или лучше вылавливать переход через 0 по положительному фронту детектора? И от этого плясать. Так как пытаюсь сделать управление симистором по шагам, разбив период 10 мс на 100 интервалов по 95 мкс, чтобы получить возможность управления двумя симисторами.
Также непонятно почему, проскакивают мерцания лампы.
Еще вопрос, имеет ли значение, если будет использоваться два таймера, в обработчике прерывания значения задержек для таймеров менять в цикле (внутри обработчика) или просто присвоением значения переменной.
 

Melandr

Member
Сделал по этому алгоритму реализацию диммера 1 канал, но думаю добавить будет несложно. Еще правда не проверял, как будет работать диммер, вечером если успею зашить в ESP, отпишусь
Код:
#include "ets_sys.h"
#include "gpio.h"

#define INTC_EDGE_EN  (*(volatile uint32_t *)0x3FF00004)
#define TIMER_LOAD (*(volatile uint32_t *)0x60000600)
#define TIMER_COUNT (*(volatile uint32_t *)0x60000604)
#define TIMER_CTRL (*(volatile uint32_t *)0x60000608)
#define TIMER_INT  (*(volatile uint32_t *)0x6000060c)
#define GPIO_OUT_W1TS (*(volatile uint32_t *)0x60000304)
#define GPIO_OUT_W1TC (*(volatile uint32_t *)0x60000308)
#define GPIO_OUTP (*(volatile uint32_t *)0x60000300)
#define GPIO_STATUS (*(volatile uint32_t *)0x6000031C)
#define GPIO_PIN2_CFG (*(volatile uint32_t *)0x60000330)
#define GPIO_STATUS_W1TC (*(volatile uint32_t *)0x60000324)

/*Relay no*/
static const int DIMMABLE_TRIAC_1 = 2; // GPIO2

/*ZCD*/
static const int ZCD_IN = 1; // GPIO1
static const int Dimmer_width = 100;

/*ZCD Variables*/
int freqStep = 87;//10 мс
int Dimmer_value_one = 20;
//int Dimmer_value_two = 20;
volatile int dim_value_one = 0;
//volatile int dim_value_two = 0;
int dimming_one = 0;
//int dimming_two = 0;
volatile boolean  zeroCrossed       = false;
volatile int ticksAfterZeroCross = 0;

void ICACHE_RAM_ATTR zero_cross_detect(void *arg) {
  (void)arg;
  uint32_t tmp = GPIO_STATUS;
  GPIO_STATUS_W1TC = tmp;
  if (tmp & BIT(ZCD_IN)) {
    zeroCrossed         = true;               // set the boolean to true to tell our dimming function that a zero cross has occured
    ticksAfterZeroCross = 0;
    dim_value_one = 0;
    GPIO_OUT_W1TC = BIT(DIMMABLE_TRIAC_1);      // turn off TRIAC (and AC)

    TIMER_LOAD = freqStep * 5;
    TIMER_CTRL = 4 // TM_DIVDED_BY_16
                 | BIT(6) // TM_AUTO_RELOAD_CNT
                 | BIT(7) //  TM_ENABLE_TIMER
                 ;
  }
}

void ICACHE_RAM_ATTR dim_check(void *arg) {
  (void)arg;

  /*For Dimmer CHANELL_1*/
  if (zeroCrossed)
  {
    dim_value_one++; // increment time step counter
   
    if (dim_value_one >= dimming_one)
    {
      GPIO_OUT_W1TS = BIT(DIMMABLE_TRIAC_1); // turn on Triac
    }
   
    if (dim_value_one >= dimming_one + 1)
    {
      zeroCrossed = false; //reset zero cross detection
      dim_value_one = 0; // reset time step counter    
    }
  }

  if (ticksAfterZeroCross++ >= 95) {
    TIMER_CTRL = 0; // stop timer
    gpio_pin_intr_state_set(ZCD_IN, GPIO_PIN_INTR_NEGEDGE);
   }
}

void dimmer_stop() {
  ets_isr_mask(BIT(ETS_FRC_TIMER1_INUM) | BIT(ETS_GPIO_INUM)); // запретить прерывания GPIOs & Timer0
  GPIO_OUT_W1TC = BIT(DIMMABLE_TRIAC_1);
  TIMER_CTRL = 0; // stop timer
}

void dimmer_start() {
  dimmer_stop();
  TIMER_COUNT = 0;
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1);
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
  gpio_output_set(0, 0, BIT(DIMMABLE_TRIAC_1), BIT(ZCD_IN));
  GPIO_PIN2_CFG &= ~BIT(DIMMABLE_TRIAC_1); // normal out (push-pull)
  gpio_pin_intr_state_set(ZCD_IN, GPIO_PIN_INTR_NEGEDGE);
  ets_isr_attach(ETS_GPIO_INUM, zero_cross_detect, NULL);
  ets_isr_attach(ETS_FRC_TIMER1_INUM, dim_check, NULL);
  INTC_EDGE_EN |= BIT(1); // + timer0

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

void setup() {
  gpio_init();
  dimmer_start();
}

void loop() {

  dimming_one = Dimmer_width - Dimmer_value_one;
}
ЗЫ: Еще вопрос, есть такой регистр
#define WDEV_NOW() (*(volatile uint32_t *)0x3ff20c00)
Я так понимаю это аналог Arduino-вского micros(). Не подскажете, в нем реально хранится значение времени со старта ESP. И сохранив предыдущее значение возможно получить время выполнения кода
 

Melandr

Member
Добрый день, уважаемые форумчане!
Кто-то может подсказать, в чем может быть прикол.
Вот код, сделанный pvvx, выше в теме. Я его немного изменил, добавил OTA, так как неудобно постоянно плату передергивать, для перепрошивки.
Код:
#include "ets_sys.h"
#include "gpio.h"
#include "OTA.h"

#define INTC_EDGE_EN  (*(volatile uint32_t *)0x3FF00004)
#define TIMER_LOAD (*(volatile uint32_t *)0x60000600)
#define TIMER_COUNT (*(volatile uint32_t *)0x60000604)
#define TIMER_CTRL (*(volatile uint32_t *)0x60000608)
#define TIMER_INT  (*(volatile uint32_t *)0x6000060c)
#define GPIO_OUT_W1TS (*(volatile uint32_t *)0x60000304)
#define GPIO_OUT_W1TC (*(volatile uint32_t *)0x60000308)
#define GPIO_OUTP (*(volatile uint32_t *)0x60000300)
#define GPIO_STATUS (*(volatile uint32_t *)0x6000031C)
#define GPIO_PIN2_CFG (*(volatile uint32_t *)0x60000330)
#define GPIO_STATUS_W1TC (*(volatile uint32_t *)0x60000324)

static const int GPIO_OUT = 2; // GPIO2
static const int GPIO_IN = 1; // GPIO1

const char* ssid = "ASUS";
const char* password = "gCU8YNZs";

uint32_t volatile pulse1_in_0us2 = 600 * 5; // in 0.2 us (0x007fffff max), min 12*5
uint32_t volatile pulse2_in_0us2 = 50 * 5; // in 0.2 us (0x007fffff max), min 12*5

//обработчик внешнего прерывания по входу
void ICACHE_RAM_ATTR GPIOs_intr_handler(void *arg)
{
  (void)arg;
  uint32_t tmp = GPIO_STATUS;
  GPIO_STATUS_W1TC = tmp;
  if (tmp & BIT(GPIO_IN)) {
    GPIO_OUT_W1TC = BIT(GPIO_OUT);
    TIMER_LOAD = pulse1_in_0us2;
    TIMER_COUNT = 0;
    TIMER_CTRL = 4 // TM_DIVDED_BY_16
                 //| BIT(6) // TM_AUTO_RELOAD_CNT
                 | BIT(7) //  TM_ENABLE_TIMER
                 ;
  }
}

//обработчик прерывания аппаратного таймера
void ICACHE_RAM_ATTR hw_test_timer_cb(void *arg) {
  (void)arg;
  if (GPIO_OUTP & BIT(GPIO_OUT)) {      //если симистор включен
    GPIO_OUT_W1TC = BIT(GPIO_OUT);      //выключаем симистор
    TIMER_CTRL = 0; // stop timer
    gpio_pin_intr_state_set(GPIO_IN, GPIO_PIN_INTR_POSEDGE);
  } else {
    GPIO_OUT_W1TS = BIT(GPIO_OUT);    //если симистор выключен, включаем
    TIMER_LOAD = pulse2_in_0us2;      //загружаем в таймер значение времени импульса открытия
  }
}

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

void setup() {
  gpio_init();

  ets_isr_mask(BIT(ETS_FRC_TIMER1_INUM) | BIT(ETS_GPIO_INUM)); // запретить прерывания GPIOs & Timer0
  GPIO_OUT_W1TC = BIT(GPIO_OUT);
  TIMER_CTRL = 0; // stop timer

  TIMER_COUNT = 0;
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1);
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
  gpio_output_set(0, 0, BIT(GPIO_OUT), BIT(GPIO_IN));
  GPIO_PIN2_CFG &= ~BIT(GPIO_OUT); // normal out (push-pull)
  gpio_pin_intr_state_set(GPIO_IN, GPIO_PIN_INTR_POSEDGE);
  ets_isr_attach(ETS_GPIO_INUM, GPIOs_intr_handler, NULL);
  ets_isr_attach(ETS_FRC_TIMER1_INUM, hw_test_timer_cb, NULL);
  INTC_EDGE_EN |= BIT(1); // + timer0
  ets_isr_unmask(BIT(ETS_FRC_TIMER1_INUM) | BIT(ETS_GPIO_INUM)); // разрешить прерывания GPIOs & Timer0

  WiFi_init();
  OTA_init();
}

void loop() {
  ArduinoOTA.handle();

}
Ко входу GPIO1 подключен детектор перехода через 0 сетевого напряжения. Установлено прерывание по входу по положительному изменению состояния. Ниже осциллограмма, полученная по этому коду
SDS00002.png
SDS00003.png
И мы видим два импульса, я так понимаю, прерывание срабатывает и по положительному, и по отрицательному переходу. Почему?
 
Сверху Снизу