Плавное изменение оборотов асинхронного вентилятора переменного тока с помощью Arduino.

zsm@nxt.ru
Offline
Зарегистрирован: 27.05.2013

  Здравствуйте уважаемые форумчане.Обращаюсь с такой проблемой:

  Недавно решил своими силами сделать устройство позволяющее плавно менять скорость вентилятора(асинхронного  220 V) в зависимости от температуры.

   Для начала решил подключить датчик температуры DS18B20 и прописать логику, для проверки использовал стандартный ШИМ arduino + двигатель постоянного тока через MOSFET. Результат меня более  чем устроил, все завелось с пол пинка и олично выполняло свои функции.

  Датчик температуры  DS18B20 , использовал  стандартные библиотеки.
Первый потенциометр VAR_REZ задаёт температуру включения вентиляции (на наименьших оборотах) , второй потенциометр VAR_REZ2 задает температуру на которой вентилятор выйдет на максимальные обороты. Соотношением этих двух резисторов можно довольно тонко настроить соотношение эффективности охлаждения к уровню шума.

вот код: (сильно не ругайте он первый у меня:)

#include <OneWire.h>
#include <DallasTemperature.h>
#define VAR_REZ A0
#define VAR_REZ2 A1
#define ONE_WIRE_BUS 10//назначение термодатчика на пин 10
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
int led = 13;
int pwm = 9;

void setup(void)
{
pinMode(led, OUTPUT);
pinMode(pwm, OUTPUT);
Serial.begin(9600);
Serial.println("Start");
analogWrite(pwm, 255);
delay(2000);
sensors.begin();
}


void loop(void)

{

int valrez = analogRead(VAR_REZ);
int valrez2 = analogRead(VAR_REZ2);
int tempmin = map(valrez,147,1023,2000,5000) ;
int tempmax = map(valrez2,135,1023,2000,5000) ;
Serial.print(" Requesting temperatures...");
sensors.requestTemperatures(); // Send the command to get temperatures
Serial.println("DONE");
Serial.print("min temp-");
Serial.println(tempmin);
Serial.print("max temp-");
Serial.println(tempmax);
Serial.println(valrez);
Serial.println(valrez2);

int pwmval = map(sensors.getTempCByIndex(0)*100,tempmin,tempmax,10,255);
Serial.print("pwm-");
Serial.println(pwmval);


if (sensors.getTempCByIndex(0)*100<tempmin)
{analogWrite(pwm, 0);}

else if (sensors.getTempCByIndex(0)*100>tempmax)
{analogWrite(pwm, 255);}

else if (sensors.getTempCByIndex(0)*100>tempmin)
{analogWrite(pwm, pwmval);}


Serial.print("Temperature: ");
Serial.print(sensors.getTempCByIndex(0));
if (sensors.getTempCByIndex(0)*100>tempmin) digitalWrite(led, HIGH);
if (sensors.getTempCByIndex(0)*100<tempmin) digitalWrite(led, LOW);


}

Вдохновлённый первым успехом решил начинать с 220v... И тут начались настоящие траблы))Первым ударом было то что придётся отказаться от любимой функции analogWrite,вторым-то что придётся мутить обратную связь и.т.д.

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

В варианте с стандартным ШИМом такого не наблюдалось.Всю голову сломал то ли значение Dimmer1 сбрасывается почему то, при опросе датчика температуры, или конфликт библиотек.( может <DallasTemperature.h> тоже timer 1 использует )

но это всё лиш догадки...

В общем даже не знаю в какую сторону смотреть т.к. это первый проэкт на МК.

Буду рад любым предложениям, советам, критике , примерам)

Всем заранее спасибо)

Вот код переделаный:

Здесь все библиотеки использованые в проэкте.

#include "nanopins.h" //быстрое управление пинами
#include <TimerOne.h> //использует Timer1
#include <OneWire.h>
#include <DallasTemperature.h>
#define VAR_REZ A0
#define VAR_REZ2 A1
#define ONE_WIRE_BUS 10//назначение термодатчика на пин 10
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
int led = 13;
volatile uint8_t tic, Dimmer1, Dimmer2, Dimmer3;

void setup() 
{ 
  D4_Out; //Настраиваем порт на выход Dimmer1
  D5_Out; //Настраиваем порт на выход Dimmer2
  D6_Out; //Настраиваем порт на выход Dimmer3
  D4_Low; D5_Low; D6_Low; //установить на выходах низкий уровень сигнала
  D2_In; //настраиваем порт на вход для отслеживания прохождения сигнала через ноль
  pinMode(led, OUTPUT);
  Serial.begin(9600);
  Serial.println("Start");
  sensors.begin();   
   
  attachInterrupt(0, detect_up, FALLING);  // настроить срабатывание прерывания int0 на pin 2 

  Timer1.initialize(40);              // Интервал срабатывания таймера в мкс 
  Timer1.attachInterrupt(halfcycle);   //будет вызыватся каждый раз при отсчете заданого времени
  Timer1.stop();
}
//********************обработчики прерываний*******************************
void halfcycle()  //прерывания таймера
{ 
  tic++;  //счетчик  
  if(Dimmer1 < tic ) D4_High; //управляем выходом
  if(Dimmer2 < tic ) D5_High;  //управляем выходом
  if(Dimmer3 < tic ) D6_High;  //управляем выходом 
}

void  detect_up()  // обработка внешнего прерывания. Сработает по переднему фронту синусоиды
{  
 tic=0;             //обнулить счетчик
 Timer1.resume();   //запустить таймер
 attachInterrupt(0, detect_down, RISING);  //перепрограммировать прерывание на другой обработчик
}  

void  detect_down()  // обработка внешнего прерывания. Сработает по заднему фронту синусоиды
{   
 Timer1.stop(); //остановить таймер
 D4_Low; D5_Low; D6_Low; //логический ноль на выходы
 tic=0;       //обнулить счетчик
 attachInterrupt(0, detect_up, FALLING); //перепрограммировать прерывание на другой обработчик
} 
//*************************************************************************
void loop() 
{
  
  int valrez = analogRead(VAR_REZ);
  int valrez2 = analogRead(VAR_REZ2);
  int tempmin = map(valrez,147,1023,2000,5000) ;
  int tempmax = map(valrez2,135,1023,2000,5000) ;
  Serial.print("   Requesting temperatures...");
  sensors.requestTemperatures(); // Send the command to get temperatures
  Serial.println("DONE");
  Serial.print("min temp-");
  Serial.println(tempmin);
  Serial.print("max temp-");
  Serial.println(tempmax);
  Serial.println(valrez);
  Serial.println(valrez2); 
  
  int pwmval = map(sensors.getTempCByIndex(0)*100,tempmin,tempmax,150,0);
  Serial.print("pwm-");
  Serial.println(pwmval);
  
  
  if (sensors.getTempCByIndex(0)*100<tempmin) 
    {Dimmer1=255;}
  
  else if (sensors.getTempCByIndex(0)*100>tempmax) 
    {Dimmer1=0;}
  
  else if  (sensors.getTempCByIndex(0)*100>tempmin)
    {Dimmer1=pwmval;}
 
 
  Serial.print("Temperature: ");
  Serial.print(sensors.getTempCByIndex(0));
   if  (sensors.getTempCByIndex(0)*100>tempmin) digitalWrite(led, HIGH);
   if  (sensors.getTempCByIndex(0)*100<tempmin) digitalWrite(led, LOW);
      
  
  }  

 

 

 

maksim
Offline
Зарегистрирован: 12.02.2012

Открываете файл OneWire.cpp и все строки содержащие noInterrupts(); комментируете:

uint8_t OneWire::reset(void)
{
	IO_REG_TYPE mask = bitmask;
	volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;
	uint8_t r;
	uint8_t retries = 125;

	//noInterrupts();
	DIRECT_MODE_INPUT(reg, mask);
	interrupts();
	// wait until the wire is high... just in case
	do {
		if (--retries == 0) return 0;
		delayMicroseconds(2);
	} while ( !DIRECT_READ(reg, mask));

	//noInterrupts();
	DIRECT_WRITE_LOW(reg, mask);
	DIRECT_MODE_OUTPUT(reg, mask);	// drive output low
	interrupts();
	delayMicroseconds(500);
	//noInterrupts();
	DIRECT_MODE_INPUT(reg, mask);	// allow it to float
	delayMicroseconds(80);
	r = !DIRECT_READ(reg, mask);
	interrupts();
	delayMicroseconds(420);
	return r;
}

и т.д.

И так по всему файлу. Сохраняете файл. Перезапускаете IDE. Компилируете.

Но учтите датчик температуры начнет лагать и температура будет с ошибками, которые прийдется фильтровать.

zsm@nxt.ru
Offline
Зарегистрирован: 27.05.2013

Большое спасибо, буду пробовать :)

zsm@nxt.ru
Offline
Зарегистрирован: 27.05.2013

Опробовал Ваш рецепт, есть провижения некоторые ) прерывание по опросу датчика действительно исчезли. Но вентилятор стал жить своей жизнью- хаотично прибавляет и снижает скорость и.т.д

 Причем дело не в лагах датчика , они появились конечно но проскакивают довольно редко-раз в 20 секунд примерно появляется значение -127, в промежутке между лагами пока температура адекватная посылается вентик успевает несколько раз сбросить и набрать скорость))

Как думаете может  легче датчик сменить на  DTH11 например? он вроде по другому протоколу работает.

maksim
Offline
Зарегистрирован: 12.02.2012

Так может тогда дело не в датчике, а в алгоритме регулирования? DTH11 тоже работает по асинхронному протоколу.

zsm@nxt.ru
Offline
Зарегистрирован: 27.05.2013

Может быть , но первый скетч в первом посте абсолютно рабочий, и тестовый код диммера тоже нареканий не вызывает , но совместить их никак не выходит :(   короче  надо мне  матчасть учить прежде чем за такие проэкты браться))

код диммера:



#define VAR_REZ A0
#include "nanopins.h" //быстрое управление пинами
#include <TimerOne.h> //использует Timer1
volatile uint8_t tic, Dimmer1, Dimmer2, Dimmer3;


void setup() 
{ 
  
  Serial.begin(9600);
  D4_Out; //Настраиваем порт на выход Dimmer1
  D5_Out; //Настраиваем порт на выход Dimmer2
  D6_Out; //Настраиваем порт на выход Dimmer3
  D4_Low; D5_Low; D6_Low; //установить на выходах низкий уровень сигнала
  D2_In; //настраиваем порт на вход для отслеживания прохождения сигнала через ноль  
   
  attachInterrupt(0, detect_up, FALLING);  // настроить срабатывание прерывания int0 на pin 2 

  Timer1.initialize(40);              // Интервал срабатывания таймера в мкс 
  Timer1.attachInterrupt(halfcycle);   //будет вызыватся каждый раз при отсчете заданого времени
  Timer1.stop();
}
//********************обработчики прерываний*******************************
void halfcycle()  //прерывания таймера
{ 
  tic++;  //счетчик  
  if(Dimmer1 < tic ) D4_High; //управляем выходом
  if(Dimmer2 < tic ) D5_High;  //управляем выходом
  if(Dimmer3 < tic ) D6_High;  //управляем выходом 
}

void  detect_up()  // обработка внешнего прерывания. Сработает по переднему фронту синусоиды
{  
 tic=0;             //обнулить счетчик
 Timer1.resume();   //запустить таймер
 attachInterrupt(0, detect_down, RISING);  //перепрограммировать прерывание на другой обработчик
}  

void  detect_down()  // обработка внешнего прерывания. Сработает по заднему фронту синусоиды
{   
 Timer1.stop(); //остановить таймер
 D4_Low; D5_Low; D6_Low; //логический ноль на выходы
 tic=0;       //обнулить счетчик
 attachInterrupt(0, detect_up, FALLING); //перепрограммировать прерывание на другой обработчик
} 
//*************************************************************************
void loop() 
{  
  
   int valrez = analogRead(VAR_REZ);
   int tempmin = map(valrez,147,1023,200,0) ;
   Dimmer1=tempmin;
    Serial.println(tempmin); 
   
   
   
  
   
}  

 

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

Я бы предложил сделать некоторое сглаживание результата измерения температуры, измерение ее раз в 20 - 30 секунд, а не в каждом цикле, ну и, для обеспечения плавности регулирования, на вход оптопары - воткнул бы конденсатор, который обеспечил бы 1 - 3 - 5 секунд задержки после снятия сигнала с ножки D4.

maksim
Offline
Зарегистрирован: 12.02.2012

Вы ошибаетесь, нельзя сглаживать управляющий сигнал при фазовом управлении. Это не аналоговый диммер.

 

По поводу того что двигатель не стабильно работает, не в одном из примеров кода нет ни фильтрации, ни усреднения, ни нормального регулирования.

paf
Offline
Зарегистрирован: 25.01.2013

Во-от это забавно. Регулировать обороты асинхронника с помощью ШИМа!   ????

На практике однофазные асинхронники позволяют в небольших пределах регулировать мощность. Но обороты.... ?????

zsm@nxt.ru
Offline
Зарегистрирован: 27.05.2013

Спасибо за ответы)

maksim пишет:
По поводу того что двигатель не стабильно работает, не в одном из примеров кода нет ни фильтрации, ни усреднения, ни нормального регулирования.

Совершенно согласен , это только наброски , но повторюсь что по отдельности эти скетчи меня полностью устраивают. Если бы удалось их совместить , то продолжил бы работу над устранением мелких багов.

  Если у кого есть свой взгляд на решение задачи (Плавное изменение оборотов асинхронного вентилятора переменного тока с помощью Arduino) , будет очень интересно услышать мнения. Не в коем случае не считаю своё решение верным, просто эксперементирую и смотрю на результаты.

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

paf пишет:

На практике однофазные асинхронники позволяют в небольших пределах регулировать мощность. Но обороты.... ?????

Обороты за счет "скольжения" получатся... А вообще надо автору второй термометр, благо по 1-wire их можно цеплять пачками, поставить непосредственно на корпус двигателя, чтобы по перегреву включать его на полную мощность без искривления синусоиды питания...

zsm@nxt.ru
Offline
Зарегистрирован: 27.05.2013

Заработало !  Спасибо всем за помощь )
Пришлось датчик температуры по другому подключить(в коде все изменения видно). подсказали добрые люди с форума где димер брал.
Код конечно еще в порядок приводить, но в целом всё работает .

Предложения по оптимизации и улучшению приветствуются))

#include <MsTimer2.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "nanopins.h" //быстрое управление пинами
#include <TimerOne.h> //использует Timer1

#define VAR_REZ A0
#define VAR_REZ2 A1
#define LM_35  A5
#define ONE_WIRE_BUS  10    // датчик DS1820 DATA подкл. на pin 10
#define RES_TEMP      12    // разрешение (точность) темп. датчика в битах

volatile uint8_t tic, Dimmer1, Dimmer2, Dimmer3;
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress Thermometer; // адрес устройства

float TempC=0;
int led = 13;

void setup()

{
    Serial.begin(9600);
    sensors.begin();                                 // Start up the library
    sensors.getAddress(Thermometer, 0);              // получить адрес DS18B20 (0 - 1 датчик)
    sensors.setResolution(Thermometer, RES_TEMP);    // установить разрешение (точность)
    sensors.setWaitForConversion(false);             // отключить ожидание при изм.темп.
                                                     // время на изм. должно быть > 750ms
    sensors.requestTemperatures();                 // Команда на измерение температуры,
                                                     // выборка произойдет по прерыванию таймера через 2 сек (первый раз)

    MsTimer2::set(2000, Tizm);      // установка таймера на 2 сек. для изм. температуры
    MsTimer2::start();              // запустить таймер
  D4_Out; //Настраиваем порт на выход Dimmer1
  D5_Out; //Настраиваем порт на выход Dimmer2
  D6_Out; //Настраиваем порт на выход Dimmer3
  D4_Low; D5_Low; D6_Low; //установить на выходах низкий уровень сигнала
  D2_In; //настраиваем порт на вход для отслеживания прохождения сигнала через ноль
  pinMode(led, OUTPUT);
  Serial.begin(9600);
  Serial.println("Start");
 
   
  attachInterrupt(0, detect_up, FALLING);  // настроить срабатывание прерывания int0 на pin 2

  Timer1.initialize(40);              // Интервал срабатывания таймера в мкс
  Timer1.attachInterrupt(halfcycle);   //будет вызыватся каждый раз при отсчете заданого времени
  Timer1.stop();
}
//********************обработчики прерываний*******************************
void halfcycle()  //прерывания таймера
{
  tic++;  //счетчик  
  if(Dimmer1 < tic ) D4_High; //управляем выходом
  if(Dimmer2 < tic ) D5_High;  //управляем выходом
  if(Dimmer3 < tic ) D6_High;  //управляем выходом
}

void  detect_up()  // обработка внешнего прерывания. Сработает по переднему фронту синусоиды
{  
 tic=0;          //обнулить счетчик
 Timer1.resume();   //запустить таймер
 attachInterrupt(0, detect_down, RISING);  //перепрограммировать прерывание на другой обработчик
}  

void  detect_down()  // обработка внешнего прерывания. Сработает по заднему фронту синусоиды
{  
 Timer1.stop(); //остановить таймер
 D4_Low; D5_Low; D6_Low; //логический ноль на выходы
 tic=0;    //обнулить счетчик
 attachInterrupt(0, detect_up, FALLING); //перепрограммировать прерывание на другой обработчик
}

void Tizm() //обработка прерывания таймера - изм температуры
{
      TempC = sensors.getTempC(Thermometer);    // Получить температуру в градусах
    sensors.requestTemperatures();        // Команда на измерение температуры для следующего раза
}


//*************************************************************************
void loop()

{
 
 
 
 
  int valrez = analogRead(VAR_REZ);
  int valrez2 = analogRead(VAR_REZ2);
  int tempmin = map(valrez,147,1023,2000,5000) ;
  int tempmax = map(valrez2,135,1023,2000,5000) ;
  Serial.print("   Requesting temperatures...");
  Serial.println("DONE");
  Serial.print("min temp-");
  Serial.println(tempmin);
  Serial.print("max temp-");
  Serial.println(tempmax);
  Serial.println(valrez);
  Serial.println(valrez2);
 
  int pwmval = map(TempC*100,tempmin,tempmax,110,60);
  Serial.print("pwm-");
  Serial.println(pwmval);
 
 
  if (TempC*100<tempmin)
    {Dimmer1=255;}
 
  else if (TempC*100>tempmax)
    {Dimmer1=0;}
 
  else if  (TempC*100>tempmin)
    {Dimmer1=pwmval;}
 
 
  Serial.print("Temperature: ");
  Serial.print(TempC);
   if  (TempC*100>tempmin) digitalWrite(led, HIGH);
   if  (TempC*100<tempmin) digitalWrite(led, LOW);
   
   delay(200);
     
 
  }  

 

Sinister
Offline
Зарегистрирован: 14.06.2012

Обороты асинхронного двигателя регулируются изменением частотыzsm@nxt.ru,  у Вас может быть коллекторный двигатель?