АС диммер, странная работа плавного пуска.

Dns7

✩✩✩✩✩✩✩
8 Сен 2024
13
0
Добрый день. Есть диммер переменного тока на тини13, для плавного запуска торцовочной пилы. На данный момент плата диммера и блок питания подключены после силовой кнопки торцовки. То есть при нажатии кнопки плата включается и запускает пилу, при отжатии плата полностью обесточивается. Подумал что не совсем правильно постоянно вкл/откл плату управления и хочу блок питания включить до силовой кнопки и пока кабель пилы включена в розетку плата с тини13 всегда включена. Нажимая силовую кнопку подаем питание только на силовую часть, но при этом нужно как то сбрасывать duty(коэф заполнения, 0-255) при отжатой силовой кнопке, если этого не делать при втором нажатии duty будет 255(симистор открыт полностью) и на мотор сразу подается 230В.
Сделал так, переменную my_tmr сравниваю с millis и если разность более 10мсек(полупериод синусоиды 50Гц), я поставил 15мсек, то duty сбрасываем в ноль иначе плавно увеличиваем duty до 255.
Работает ПОЧТИ как надо. Если точнее после подачи напряжения на силовую часть плавно включается какое то время работает, сбрасывается в ноль, опять плавно включается и так по кругу. Все это дело происходит с рандомной частотой. Не пойму в чем проблема.

P.S. Тесты на лампе накаливания. При отсутствии силового напряжения(230В) на входе детекции нуля +5В постоянно, по схеме это видно. Класс таймера брал с примеров GyverDimmer. Отлаживаю на Arduino Nano, на самом плавном пуске стоит Tiny13.

C++:
class TimerUs {
  public:
    bool ready() {
      if (state && micros() - tmr >= prd) {
        tmr = micros();
        return true;
      }
      return false;
    }
    void stop() {
      state = false;
    }
    void restart() {
      tmr = micros();
      state = true;
    }
    void setPeriod(uint32_t period) {
      restart();
      prd = period;
      if (prd == 0) prd++;
    }

  private:
    bool state = 0;
    uint32_t tmr = 0, prd = 0;
};
TimerUs timer;

// -------- НАСТРОЙКИ ---------
#define FB_pin 2      
#define Dimmer_pin 13  

// ------- БИБЛИОТЕКИ -------
#include <GyverDimmer.h>
Dimmer<Dimmer_pin> dim;   // указать пин диммера

// ------- ПЕРЕМЕННЫЕ -------
volatile uint8_t duty = 0;     //  Начальное значение мощности
uint32_t my_tmr = 0;


// --------- SETUP ----------
void setup() {
  Serial.begin(9600);
  //Инициализация прерываний
  attachInterrupt(0, myInt, RISING); //если использ. прерыв. в стиле ардуино
}

void myInt() {
  // вызывать в прерывании детектора нуля
  // если tickZero() - true - нужно перезапустить таймер с периодом getPeriod()
  if (dim.tickZero()) timer.setPeriod(dim.getPeriod()); //
  else timer.restart();
  my_tmr = millis();
}


//-----------Основной цикл------------
void loop() {
  if (timer.ready()) {  // если таймер готов(отсчитало нужное время)
    dim.tickTimer();    // вызвать tickTimer(подать сигнал на пин диммера)
    timer.stop();       // остановить таймер      
  }  

  if(millis() - my_tmr >= 15)  duty = 0;  
    else softStart();  
  dim.write(duty);
  debug();    
}  

//------------------ФУНКЦИИ-----------------
void softStart()  {
  static uint32_t tmr;
  if (millis() - tmr >= 20) {
    tmr = millis();
   if(duty < 255) duty += 1;
  }
}

void debug()  {
  static uint32_t tmr;
  if (millis() - tmr >= 50) {
    tmr = millis();
   
    Serial.print("Коэф. заполнения - ");
    Serial.print(duty);
    Serial.println(". ");

  }
}
 

Вложения

Изменено:

Bruzzer

★★★✩✩✩✩
23 Май 2020
430
129
@Dns7,
Добавьте в setup вывод в сериал сообщения о старте.
Может у вас NANO перезапускается из за наводок.

И вставляйте код, как код, читать не удобно без отступов.. Иконка в меню </>
 

Dns7

✩✩✩✩✩✩✩
8 Сен 2024
13
0
@Bruzzer, Вывод в сериал прописал, выводится 1раз при запуске МК хотя нагрузка также перезапускается.
 

poty

★★★★★★✩
19 Фев 2020
3,188
928
@Dns7, попробуйте добавить volatile к определению my_tmr.
Также я бы более внимательно относился к инициализации переменных.
 

Dns7

✩✩✩✩✩✩✩
8 Сен 2024
13
0
@poty, volatile добавил, изменений нет. Что конкретно имеете ввиду под - более внимательно инициализировать?
 

Dns7

✩✩✩✩✩✩✩
8 Сен 2024
13
0
@poty, К my_tmr
C++:
// ------- ПЕРЕМЕННЫЕ -------
volatile uint8_t duty = 0;     //  Начальное значение мощности
volatile uint32_t my_tmr = 0;
 

poty

★★★★★★✩
19 Фев 2020
3,188
928
@Dns7, тогда, если я от невнимательности ничего не проглядел в коде, причиной может быть пропуск прерывания. Попробуйте сделать задержку в 100мс.
 

Dns7

✩✩✩✩✩✩✩
8 Сен 2024
13
0
@poty, Пробовал, и 50 и 100мс. Частота сети настолько не плавает, по идее и 15мс это уже с запасом.
 

poty

★★★★★★✩
19 Фев 2020
3,188
928
@Dns7, частота, понятное дело, не плавает, а вот помехи - есть. Но если не помогает увеличение интервала, то остаётся ставить везде отладку и смотреть на результат. Отлавливать нужно факт разного значения millis() - my_tmr в if таймера выключения и внутри него. Возможно, сделать несколько интервалов и посмотреть, реально ли они все срабатывают?
Также проверить, не подключено ли что либо к пину, вызывающему прерывание.
 

Dns7

✩✩✩✩✩✩✩
8 Сен 2024
13
0
В основном цикле, в условие if (millis() - tmr >= 1000) добавил вывод в монитор порта, и даже когда разность не больше этого числа все равно выкидывает внутрь фигурных скобок после if. Скрин приложил.

C++:
  if(millis() - my_tmr >= 1000)
  {
    duty = 0;              
    Serial.print("duty = 0; ");
    Serial.print("millis() - my_tmr = ");
    Serial.println(millis() - my_tmr);
  }     
   else softStart();     
 
   dim.write(duty);
Насчет помех, схема в целом собрана аккуратно. И ничем кроме диммера не управляет.
 

Вложения

Dns7

✩✩✩✩✩✩✩
8 Сен 2024
13
0
@Старик Похабыч, Таймер сбрасывается в обработчике прерывания. Вы это имели ввиду?
C++:
void myInt() {
  // вызывать в прерывании детектора нуля
  // если tickZero() - true - нужно перезапустить таймер с периодом getPeriod()
  if (dim.tickZero()) timer.setPeriod(dim.getPeriod()); //
  else timer.restart();
  my_tmr = millis();
}
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
430
129
@Dns7,
Если вы работаете с переменной изменяемой в int, и эта переменная больше, чем разрядность МК, то надо запрещать прерывания.
Т.к. НАНО восьми битовый, то при обращениии ко всему больше байта надо запрещать прерывания. Например:
C++:
cli();
auto tmp =  my_tmr;
sei();
if(millis() - tmp >= 1000)
{
  duty = 0;           
  Serial.print("duty = 0; ");
  Serial.print("millis() - my_tmr = ");
  Serial.println(millis() - tmp);
}
Дополнение. Если переменная читается в int и изменяется вне int, то перед изменением тоже надо запрещать прерывания.
В общем надо учитывать возможность срабатывания прерывания в неподходящий момент и при чтении и при записи.
 
Изменено:

poty

★★★★★★✩
19 Фев 2020
3,188
928
@Bruzzer, да, по идее это должно было исключиться переводом my_tmp в volatile, но по какой-то причине не произошло. Можно попробовать с запретом прерываний на момент чтения переменной, согласен.
Второй спорный момент - использование millis внутри прерывания. Я бы заменил на простой байтовый счётчик и проверял+сбрасывал его каждые, допустим, 50мс. Не уверен, что в этом дело, но почему бы не попробовать.
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
430
129
по идее это должно было исключиться переводом my_tmp в volatile,
Это не связанные между собой вещи. Т.е. volatile, не влияет на атомарность и наоборот. (Атомарность - это умное название свойства действия , которое не должно прерываться)
Второй спорный момент - использование millis внутри прерывания.
В данном случае я не вижу ничего спорного. Прерывание не продолжительное по времени. (И обычно millis в прерывании это нормально).
 

poty

★★★★★★✩
19 Фев 2020
3,188
928
volatile, не влияет на атомарность
не совсем. Такая опция предполагает, что операция будет выполнятся с цельной переменной из памяти. Другой вопрос: при вычислении выражения само обращение к переменной будет осуществляться не один раз, соответственно, скорее всего, дело решилось бы использованием локальной переменной (типа tmp в примере с запретом прерываний), но правильнее всё же запретить прерывания.
обычно millis в прерывании это нормально
Да, но в обработчике millis запрещаются и разрешаются прерывания, т.е. после возврата из millis прерывания снова разрешаются ещё до выхода из прерывания, в котором вызван millis.
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
430
129
что за приставка auto
На ваши вопросы ответы наверняка легко ищутся в интернете . Где именно читать, не подскажу, это по вкусу.
auto tmp = my_tmr;
auto -означает автоматический выбор типа, он будет такой же как у my_tmr;

По поводу атомарнисти. В вашем исходном коде две потенциальные ошибки.
C++:
  if(millis() - my_tmr >= 1000)
  {
    duty = 0;            
    Serial.print("duty = 0; ");
    Serial.print("millis() - my_tmr = ");
    Serial.println(millis() - my_tmr);
  }
1. Т.к. my_tmr изменяется в прерывании, то в строке 1 переменная my_tmr может иметь одно значение, а в строке 6 уже другое. Что и происходило скорее всего, и вызывало ваши вопросы.
2. Менее вероятный сценарий (с атомарностью). Т.к. my_tmr изменяется в прерывании и имеет длину 4 байта, то при чтении my_tmr в основном коде при разрешенных прерываниях, может оказаться так, что часть байт прочиталось до входа в прерывание, а часть после прерывания изменившего эти и оставшиеся байты. Например мы читаем tmp = my_tmr; При этом my_tmr = 0x000F и чтение происходит с младших байт. Мы прочитали F, и наступило прерывание, после выхода из которого my_tmr = 0x0010, мы считываем оставшиеся три байта и в результате получаем tmp = 1F
 

Dns7

✩✩✩✩✩✩✩
8 Сен 2024
13
0
@Dns7,
C++:
cli();
auto tmp =  my_tmr;
sei();
if(millis() - tmp >= 1000)
{
  duty = 0;         
  Serial.print("duty = 0; ");
  Serial.print("millis() - my_tmr = ");
  Serial.println(millis() - tmp);
}
Попробовал такой вариант, работает нормально.
C++:
  uint8_t ms = millis() & 0xFF;

  if(ms - my_tmr >= 15)
  {
    duty = 0;            
    Serial.print("duty = 0; ");
    Serial.print("millis() - my_tmr = ");
    Serial.println(ms - my_tmr);
  }    
    else softStart();
P.S. НО! судя по статье DiHalt'а, даже при работе с однобайтными переменными нужна атомарность. Т.к. операция изменения переменной делается не за одну операцию. AVR. Учебный Курс. Программирование на Си. Атомарные операции. | (easyelectronics.ru)