Разработка фена для пайки на Arduino

Дим M
Автор темы, Администратор
Администратор
Аватара
Дим M
Автор темы, Администратор
Администратор
Сообщения: 1608
Зарегистрирован: 5 апреля 2013
С нами: 10 лет 11 месяцев

#21 Дим » 23 апреля 2018, 20:13

Добавил на плату LED дисплей 128x64 1.3" SPI и транзистор c2331 для регулировки оборотов вентилятора фена. Как всегда оставляю код

Спойлер

Код: Выделить всё

#include "U8glib.h"
#include "rus6x12.h"
#include <CyberLib.h> //Библиотека от Cyber-Place.ru
#include <Encoder.h>

const uint8_t Logo[]U8G_PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0xC4,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x22,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x22,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x22,
0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x82,
0xFF, 0xFC, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x42, 0x42,
0x3E, 0x06, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x42, 0x22,
0x1E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x27, 0x74,
0x1E, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x10, 0x08,
0x1E, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x08, 0x10,
0x1E, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0,
0x1E, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x38, 0xFC, 0x3F, 0x3E, 0x0F, 0x80, 0x1E, 0x04, 0x00, 0xFC, 0x0F, 0x9F, 0x80, 0x00,
0x1E, 0x00, 0x38, 0x1C, 0x07, 0x43, 0x31, 0xC0, 0x1E, 0x04, 0x01, 0x02, 0x03, 0xA1, 0xC0, 0x00,
0x1E, 0x00, 0x3C, 0x1C, 0x07, 0x81, 0xC0, 0xE0, 0x1E, 0x0C, 0x02, 0x03, 0x81, 0xC0, 0xC0, 0x00,
0x1E, 0x00, 0x3C, 0x1C, 0x07, 0x01, 0xC0, 0x70, 0x1F, 0xFC, 0x06, 0x01, 0x81, 0x80, 0x40, 0x00,
0x1E, 0x00, 0x3C, 0x1C, 0x07, 0x01, 0xC0, 0x70, 0x1F, 0xFC, 0x06, 0x01, 0x81, 0x80, 0x60, 0x00,
0x1E, 0x00, 0x3C, 0x1C, 0x07, 0x01, 0xC0, 0x70, 0x1E, 0x0C, 0x0E, 0x01, 0x81, 0x80, 0x60, 0x00,
0x1E, 0x00, 0x3C, 0x1C, 0x07, 0x01, 0xC0, 0x70, 0x1E, 0x04, 0x0F, 0xFF, 0xC1, 0x80, 0x60, 0x00,
0x1E, 0x00, 0x38, 0x1C, 0x07, 0x01, 0xC0, 0x70, 0x1E, 0x04, 0x0E, 0x00, 0x01, 0x80, 0x60, 0x00,
0x1E, 0x00, 0x38, 0x1C, 0x07, 0x01, 0xC0, 0x70, 0x1E, 0x00, 0x0E, 0x00, 0x01, 0x80, 0x60, 0x00,
0x1E, 0x00, 0x30, 0x1C, 0x07, 0x01, 0xC0, 0x70, 0x1E, 0x00, 0x0E, 0x00, 0x01, 0x80, 0x60, 0x00,
0x1E, 0x00, 0x30, 0x1C, 0x07, 0x01, 0xC0, 0x70, 0x1E, 0x00, 0x06, 0x00, 0x01, 0x80, 0x60, 0x00,
0x1E, 0x00, 0x60, 0x1C, 0x07, 0x01, 0xC0, 0x70, 0x1E, 0x00, 0x06, 0x00, 0xC1, 0x80, 0x60, 0x00,
0x1E, 0x00, 0xC0, 0x1C, 0x07, 0x01, 0xC0, 0x70, 0x1E, 0x00, 0x06, 0x00, 0xC1, 0x80, 0x60, 0x00,
0x1E, 0x01, 0x00, 0x1C, 0x07, 0x01, 0xC0, 0x70, 0x1E, 0x00, 0x03, 0x81, 0x81, 0x80, 0x60, 0x00,
0x3E, 0x06, 0x00, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3F, 0x00, 0x01, 0xFF, 0x03, 0xC0, 0xF0, 0x00,
0xFF, 0xF8, 0x00, 0x7F, 0x1F, 0xCF, 0xFB, 0xFC, 0xFF, 0xC0, 0x00, 0x7C, 0x0F, 0xF3, 0xFC, 0x00
};

const uint8_t Termometr[]U8G_PROGMEM = {
0x18, 0x24, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x62, 0x62, 0x72, 0x72, 0x7A, 0x7A, 0x7E,
0x7E, 0x7E, 0xFF, 0x9F, 0x9F, 0xFF, 0x7E, 0x18, 
};

#define PIN_ENCODER_CLK 3
#define PIN_ENCODER_DT 5
#define PIN_ENCODER_SW 7
Encoder myEnc(PIN_ENCODER_DT, PIN_ENCODER_CLK);
//===========================================================
U8GLIB_SSD1306_128X64 u8g(A4, /* data=*/ A3, /* cs=*/ A2, /* dc=*/ A1, /* reset=*/ A0); // наш дисплей  ||
//===========================================================

int motorPin = 6;
int motorSpeed = 75;                   // Изначальная скорость двигателя
int fanPower = 115;                    // Изначальная мощность фена
int selectMode = 0;                    // Что изменять, скорость или мощность фена
int accel = 1;
int i = 0;
long oldPosition = 0;
volatile uint8_t tic, Dimmer;
// =================================== Кнопка энкодера   =====================
bool     button_state      = false;
bool     button_auto_state = false;
uint32_t ms_button         = 0;
uint32_t ms_auto_click     = 0;
// =================================== Кнопка энкодера   =====================
void draw(void) {
  u8g.setFont(u8g_font_courR24n);            // тонкие цифры
  u8g.setPrintPos(15, 22);
  u8g.print(Dimmer);

  u8g.setPrintPos(15, 64);
  u8g.print(motorSpeed);

  u8g.setPrintPos(-2, 70);
  u8g.print("*");
   
  if
(selectMode == 1 && (millis() & 512))
  {  
  u8g
.setPrintPos(78, 23);
  u8g.print(".");
  }
  if(selectMode == 2 && (millis() & 512))
  {
  u8g.setPrintPos(78, 59);
  u8g.print(".");
  }
// =================================== 
  u8g.setPrintPos(81, 6);
  u8g.setFont(rus6x12);
  u8g.print("температура");

  u8g.setPrintPos(84, 43);
  u8g.print("обороты");
// =================================== 
  u8g.setFont(u8g_font_courR14r);
  u8g.setPrintPos(95, 27);
//  u8g.print(SetPower);

  u8g.setPrintPos(95, 64);
//  u8g.print(SetSpeed);
  
// =================================== 
  u8g.drawLine(77, 0, 77, 64);      // Вертикальная линия
  u8g.drawLine(80, 32, 128, 32);    // Горизонтальная короткая линия

  u8g.drawBitmapP(0, 0, 1, 24, Termometr);
//  u8g.drawBitmapP(-2, 47, 2, 14, Cooler);
}

void setup()
{
  Dimmer = fanPower;
  Serial.begin( 9600 );
  TCCR0B = TCCR0B & 0b11111000 | 5;
  pinMode(motorPin, OUTPUT);
  pinMode(PIN_ENCODER_SW, INPUT_PULLUP); //  Кнопка энкодера
  D4_Out;  //Настраиваем порты на выход
  D4_Low;  //установить на выходах низкий уровень сигнала
  D2_In; //настраиваем порт на вход для отслеживания прохождения сигнала через ноль
  attachInterrupt(0, detect_up, LOW); // настроить срабатывание прерывания interrupt0 на pin 2 на низкий уровень
  StartTimer1(halfcycle, 40); //время для одного разряда ШИМ
  StopTimer1(); //остановить таймер
  //======================
  u8g.firstPage();  
    do 
{
  u8g.drawBitmapP(0, 12, 16, 31, Logo);    //0 - отступ слева, 12 - от верха, 16*8 ширина картинки, 31 - высота
    } while( u8g.nextPage() );
  delay (400);
  //======================  
  u8g.firstPage();  
  do 
{
    draw();
    } while( u8g.nextPage() );
  //====================== 
}
//********************обработчик и прерываний******************** ***********
void halfcycle() //прерывания таймера
{
  tic++; //счетчик
  if(Dimmer < tic ) D4_High; //управляем выходом
}
void detect_up() // обработка внешнего прерывания. Сработает по переднему фронту
{
  tic=0; //обнулить счетчик
  ResumeTimer1(); //запустить таймер
  attachInterrupt(0, detect_down, HIGH); //перепрограммировать прерывание на другой обработчик
}
void detect_down() // обработка внешнего прерывания. Сработает по заднему фронту
{
  StopTimer1(); //остановить таймер
  D4_Low; //логический ноль на выходы
  tic=0; //обнулить счетчик
  attachInterrupt(0, detect_up, LOW); //перепрограммировать прерывание на другой обработчик
}
//**************************************** *********************************
void loop()
{
// =================================== Кнопка энкодера   =====================
  uint32_t ms    = millis();
  bool pin_state = digitalRead(PIN_ENCODER_SW); 
  if
( pin_state  == LOW && !button_state && ( ms - ms_button ) > 50 )// Фиксируем нажатие кнопки  
  {
    button_state      = true;
    button_auto_state = false;  
    ms_button         
= ms;
    if(selectMode == 0)
    {
      selectMode = 1;  
      Serial
.println("Press key");
    }
    else
    
{
      selectMode = 0;
    }
  }
  if( pin_state  == LOW && ( ms - ms_button ) > 2000 && ( ms - ms_auto_click )>500 ) // После 2000 мс нажатия кнопки каждые 500 мс фиксируем событие нажатия  
  {
    button_auto_state = true;
    ms_auto_click     = ms;
//    Serial.println("Auto press key");    
  }  
  if
( pin_state == HIGH && button_state && ( ms - ms_button ) > 50  ) // Фиксируем отпускание кнопки 
  {
    button_state      = false;   
    ms_button         
= ms;
    if( !button_auto_state )
    Serial.println("No Press key");
  }
// =================================== Кнопка энкодера   =====================   
  analogWrite(motorPin, motorSpeed);  // Устанавливаем первоначальную скорость
//=== ручка энкодера/регулироака
  long newPosition = myEnc.read() / accel;    
  if 
(newPosition > oldPosition) 
  
{ 
    if 
(selectMode == 0)  
    
{  
      motorSpeed 
+= 1;                  // Прибавляем скорость
      if ( motorSpeed > 255 )
      motorSpeed = 255;
      Serial.println("1 >>>");
    }
    else
    
{
      fanPower -= 1;                   // Прибавляем мощность фена
      if ( fanPower < 0)
      fanPower = 0;   
      Serial
.println("2 >>>");  
    
}
  }
  else
  if 
(newPosition < oldPosition)
  {
    if (selectMode == 0)  
    
{       
      motorSpeed 
-= 1;                  // Убавляем скорость
      if ( motorSpeed < 75 )
      motorSpeed = 75;
      Serial.println("1 <<<");
    }
    else
    
{
      fanPower += 1;                   // Убавляем мощность фена
      if ( fanPower > 255)
      fanPower = 255;   
      Serial
.println("2 <<<");
    }
  } 
  if 
( newPosition != oldPosition)
  {
    i = (+ 1)%16;
    oldPosition = newPosition;
    analogWrite( motorPin, motorSpeed );
    Dimmer = fanPower;
  } 
  Dimmer 
= fanPower;

плата с дисплеем.JPG
плата с дисплеем
плата с дисплеем.JPG (168.94 КБ) 3613 просмотров


плата с энкодером и дисплеем.png
плата с энкодером и дисплеем
плата с энкодером и дисплеем.png (166.78 КБ) 3610 просмотров
[center]i love you [s]mxIni[/s] Mysql[/center]


Дим M
Автор темы, Администратор
Администратор
Аватара
Дим M
Автор темы, Администратор
Администратор
Сообщения: 1608
Зарегистрирован: 5 апреля 2013
С нами: 10 лет 11 месяцев

#22 Дим » 11 мая 2018, 17:12

Попалась ещё одна картинка по теме. Оставлю здесь что бы не затерялась.
Схема подключения фена.jpg
Схема подключения фена
Схема подключения фена.jpg (32.3 КБ) 3594 просмотра
[center]i love you [s]mxIni[/s] Mysql[/center]

Дим M
Автор темы, Администратор
Администратор
Аватара
Дим M
Автор темы, Администратор
Администратор
Сообщения: 1608
Зарегистрирован: 5 апреля 2013
С нами: 10 лет 11 месяцев

#23 Дим » 15 мая 2018, 16:50

Код без дисплея и схемы подключения усилителя LM358 и термопары фена

схемы подключения усилителя LM358 и термопары фена1.png
схемы подключения усилителя LM358 и термопары фена
схемы подключения усилителя LM358 и термопары фена1.png (59.02 КБ) 3583 просмотра
схемы подключения усилителя LM358 и термопары фена.png
схемы подключения усилителя LM358 и термопары фена
схемы подключения усилителя LM358 и термопары фена.png (462.84 КБ) 3583 просмотра


Без дисплея

Код: Выделить всё

#include <CyberLib.h> //Библиотека от Cyber-Place.ru
#include <Encoder.h>

#define PRINT_INTERVAL 1000UL  // периодичность вывода времени в Serial (1 cекунда)
#define SERIAL_SPEED 9600 // скорость работы Serial
#define PIN_ENCODER_CLK 3
#define PIN_ENCODER_DT 5
#define PIN_ENCODER_SW 7
Encoder myEnc(PIN_ENCODER_DT, PIN_ENCODER_CLK);

int motorPin = 6;
int motorSpeed = 75;                   // Изначальная скорость двигателя
int fanPower = 115;                    // Изначальная мощность фена
int selectMode = 0;                    // Что изменять, скорость или мощность фена
int accel = 1;
int i = 0;
long oldPosition = 0;
volatile uint8_t tic, Dimmer;
// =================================== Кнопка энкодера   =====================
bool     button_state      = false;
bool     button_auto_state = false;
uint32_t ms_button         = 0;
uint32_t ms_auto_click     = 0;

#define thermocouplePin A5     // номер порта к которому подключен выход усилителя термопары

double Ups = 4.19; //напряжение питания усилителя и ардуино
double kt = 100; //коэффициент для преобразования напряжения в температуру
double ADCmaxVal = 1023; //максимальное значение выдаваемое АЦП ардуино
double tHome = 20.00; //комнатная температура

int thermocoupleVal = 0;           //полученное с АЦП число
double t = 0; //температура спая термопары 

void setup() 
{
  pinMode(thermocouplePin, INPUT);
  Serial.begin(SERIAL_SPEED); 
  Dimmer 
= fanPower;
  Serial.begin( 9600 );
//  TCCR0B = TCCR0B & 0b11111000 | 5;
  pinMode(motorPin, OUTPUT);
  pinMode(PIN_ENCODER_SW, INPUT_PULLUP); //  Кнопка энкодера
  D4_Out;  //Настраиваем порты на выход
  D4_Low;  //установить на выходах низкий уровень сигнала
  D2_In; //настраиваем порт на вход для отслеживания прохождения сигнала через ноль
  attachInterrupt(0, detect_up, LOW); // настроить срабатывание прерывания interrupt0 на pin 2 на низкий уровень
  StartTimer1(halfcycle, 40); //время для одного разряда ШИМ
  StopTimer1(); //остановить таймер
}

//********************обработчик и прерываний******************** ***********
void halfcycle() //прерывания таймера
{
  tic++; //счетчик
  if(Dimmer < tic ) D4_High; //управляем выходом
}
void detect_up() // обработка внешнего прерывания. Сработает по переднему фронту
{
  tic=0; //обнулить счетчик
  ResumeTimer1(); //запустить таймер
  attachInterrupt(0, detect_down, HIGH); //перепрограммировать прерывание на другой обработчик
}
void detect_down() // обработка внешнего прерывания. Сработает по заднему фронту
{
  StopTimer1(); //остановить таймер
  D4_Low; //логический ноль на выходы
  tic=0; //обнулить счетчик
  attachInterrupt(0, detect_up, LOW); //перепрограммировать прерывание на другой обработчик
}
//**************************************** *********************************  
  
void loop
()
{
  printTime(PRINT_INTERVAL); // выводим время
  enc(); // выводим время
}

void enc()
{
// =================================== Кнопка энкодера   =====================
  uint32_t ms    = millis();
  bool pin_state = digitalRead(PIN_ENCODER_SW); 
  if
( pin_state  == LOW && !button_state && ( ms - ms_button ) > 50 )// Фиксируем нажатие кнопки  
  {
    button_state      = true;
    button_auto_state = false;  
    ms_button         
= ms;
    if(selectMode == 0)
    {
      selectMode = 1;  
      Serial
.println("Press key");
    }
    else
    
{
      selectMode = 0;
    }
  }
  if( pin_state  == LOW && ( ms - ms_button ) > 2000 && ( ms - ms_auto_click )>500 ) // После 2000 мс нажатия кнопки каждые 500 мс фиксируем событие нажатия  
  {
    button_auto_state = true;
    ms_auto_click     = ms;
//    Serial.println("Auto press key");    
  }  
  if
( pin_state == HIGH && button_state && ( ms - ms_button ) > 50  ) // Фиксируем отпускание кнопки 
  {
    button_state      = false;   
    ms_button         
= ms;
    if( !button_auto_state )
    Serial.println("No Press key");
  }
// =================================== Кнопка энкодера   =====================   
  analogWrite(motorPin, motorSpeed);  // Устанавливаем первоначальную скорость
//=== ручка энкодера/регулироака
  long newPosition = myEnc.read() / accel;    
  if 
(newPosition > oldPosition) 
  
{ 
    if 
(selectMode == 0)  
    
{  
      motorSpeed 
+= 1;                  // Прибавляем скорость
      if ( motorSpeed > 255 )
      motorSpeed = 255;
      Serial.println("1 >>>");
    }
    else
    
{
      fanPower -= 1;                   // Прибавляем мощность фена
      if ( fanPower < 0)
      fanPower = 0;   
      Serial
.println("2 >>>");  
    
}
  }
  else
  if 
(newPosition < oldPosition)
  {
    if (selectMode == 0)  
    
{       
      motorSpeed 
-= 1;                  // Убавляем скорость
      if ( motorSpeed < 75 )
      motorSpeed = 75;
      Serial.println("1 <<<");
    }
    else
    
{
      fanPower += 1;                   // Убавляем мощность фена
      if ( fanPower > 255)
      fanPower = 255;   
      Serial
.println("2 <<<");
    }
  } 
  if 
( newPosition != oldPosition)
  {
    i = (+ 1)%16;
    oldPosition = newPosition;
    analogWrite( motorPin, motorSpeed );
    Dimmer = fanPower;
  } 
  Dimmer 
= fanPower; 
}
 
// выводит в Serial время с периодичностью interval
void printTime(unsigned long interval){
  static unsigned long prevTime=0;
  if(millis()-prevTime>interval){
    prevTime=millis();
     
  thermocoupleVal 
= analogRead(thermocouplePin);
  t = (thermocoupleVal*Ups/ADCmaxVal)*kt+tHome;
  Serial.println(t);
 
  
}
}
[center]i love you [s]mxIni[/s] Mysql[/center]

Дим M
Автор темы, Администратор
Администратор
Аватара
Дим M
Автор темы, Администратор
Администратор
Сообщения: 1608
Зарегистрирован: 5 апреля 2013
С нами: 10 лет 11 месяцев

#24 Дим » 16 мая 2018, 14:18

Опять возникла проблема с показаниями - энкодер что то мудрил - не сам а код. Нашёл другой код, с ним стало всё нормально. Оставлю тут код что нашёл и свой код - дисплей, энкодер и диммер.
обработчик энкодера

Код: Выделить всё

/*
** Энкодер
** Для управлением яркостью LED используется энкодер Sparkfun
*/
 
int brightness 
= 120;       // яркость LED, начинаем с половины
int fadeAmount = 10;        // шаг изменения яркости LED
unsigned long currentTime;
unsigned long loopTime;
const int pin_A = 12;       // pin 12
const int pin_B = 11;       // pin 11
unsigned char encoder_A;
unsigned char encoder_B;
unsigned char encoder_A_prev=0;
 
void setup
()  {
  // declare pin 9 to be an output:
  pinMode(9, OUTPUT);         // устанавливаем pin 9 как выход
  pinMode(pin_A, INPUT);
  pinMode(pin_B, INPUT);
  currentTime = millis();
  loopTime = currentTime; 
} 
 
void loop
()  {
  currentTime = millis();
  if(currentTime >= (loopTime + 5)){ // проверяем каждые 5мс (200 Гц)
    encoder_A = digitalRead(pin_A);     // считываем состояние выхода А энкодера 
    encoder_B = digitalRead(pin_B);     // считываем состояние выхода B энкодера    
    if((!encoder_A) && (encoder_A_prev)){    // если состояние изменилось с положительного к нулю
      if(encoder_B) {
        // выход В в полож. сост., значит вращение по часовой стрелке
        // увеличиваем яркость, не более чем до 255
        if(brightness + fadeAmount <= 255) brightness += fadeAmount;               
      
}   
      else 
{
        // выход В в 0 сост., значит вращение против часовой стрелки     
        // уменьшаем яркость, но не ниже 0
        if(brightness - fadeAmount >= 0) brightness -= fadeAmount;               
      
}   
 
    
}   
    encoder_A_prev 
= encoder_A;     // сохраняем значение А для следующего цикла 
     
    analogWrite
(9, brightness);   // устанавливаем яркость на 9 ножку
    
    loopTime 
= currentTime;
  }                       
}

Мой код

Код: Выделить всё

#include "U8glib.h"
#include "rus6x12.h"
#include <CyberLib.h> //Библиотека от Cyber-Place.ru
//===========================================================
U8GLIB_SSD1306_128X64 u8g(A4, /* data=*/ A3, /* cs=*/ A2, /* dc=*/ A1, /* reset=*/ A0); // наш дисплей  ||
//===========================================================
volatile uint8_t tic, Dimmer;
int fanPower = 115;                    // Изначальная мощность фена
int stepFen = 5;                       // шаг изменения мощности
// ===================================  энкодер   ===================== 
unsigned long currentTime;
unsigned long loopTime;
#define PIN_ENCODER_CLK 3
#define PIN_ENCODER_DT 5
#define PIN_ENCODER_SW 7
const int pin_A = 3;   
const int pin_B 
= 5;   
unsigned char ENCODER_CLK
;
unsigned char ENCODER_DT;
unsigned char ENCODER_CLK_prev=0;
int selectMode = 0;                    // Что изменять, скорость или мощность фена
// ===================================  дисплей   =====================
void draw(void) {
  u8g.setFont(u8g_font_courR24n);            // тонкие цифры
  u8g.setPrintPos(15, 22);
  u8g.print(fanPower);

  u8g.setPrintPos(15, 64);
  u8g.print(stepFen);

  u8g.setPrintPos(-2, 70);
  u8g.print("*");
   
  if
(selectMode == 1 && (millis() & 512))
  {  
  u8g
.setPrintPos(78, 23);
  u8g.print(".");
  }
  if(selectMode == 2 && (millis() & 512))
  {
  u8g.setPrintPos(78, 59);
  u8g.print(".");
  }
// =================================== 
  u8g.setPrintPos(81, 6);
  u8g.setFont(rus6x12);
  u8g.print("температура");

  u8g.setPrintPos(84, 43);
  u8g.print("обороты");
// =================================== 
  u8g.setFont(u8g_font_courR14r);
  u8g.setPrintPos(95, 27);
//  u8g.print(SetPower);

  u8g.setPrintPos(95, 64);
//  u8g.print(SetSpeed);
  
// ===================================    ===================== 
  u8g.drawLine(77, 0, 77, 64);      // Вертикальная линия
  u8g.drawLine(80, 32, 128, 32);    // Горизонтальная короткая линия

//  u8g.drawBitmapP(0, 0, 1, 24, Termometr);
//  u8g.drawBitmapP(-2, 47, 2, 14, Cooler);
}
 
void setup
()  {
// =================================== Диммер  ===================== 
//TCCR0B = TCCR0B & 0b11111000 | 5;
  Dimmer = fanPower;
  D4_Out;  //Настраиваем порты на выход
  D4_Low;  //установить на выходах низкий уровень сигнала
  D2_In; //настраиваем порт на вход для отслеживания прохождения сигнала через ноль
  attachInterrupt(0, detect_up, LOW); // настроить срабатывание прерывания interrupt0 на pin 2 на низкий уровень
  StartTimer1(halfcycle, 40); //время для одного разряда ШИМ
  StopTimer1(); //остановить таймер
// ===================================  энкодер   ===================== 
  pinMode(PIN_ENCODER_CLK, INPUT);
  pinMode(PIN_ENCODER_DT, INPUT);
  pinMode(PIN_ENCODER_SW, INPUT_PULLUP); //  Кнопка энкодера
  currentTime = millis();
  loopTime = currentTime; 
  Serial
.begin(9600);           // для отладки
//======================  Обновление дисплея ======================
        u8g.firstPage();  
        do 
{draw();} while( u8g.nextPage() );
//======================  Обновление дисплея ======================
} 

void loop
()  
{
  currentTime = millis();
  if(currentTime >= (loopTime + 5))
  { // проверяем каждые 5мс (200 Гц)
    ENCODER_CLK = digitalRead(PIN_ENCODER_CLK);        // считываем состояние выхода А энкодера 
    ENCODER_DT = digitalRead(PIN_ENCODER_DT);         // считываем состояние выхода B энкодера    
    if((!ENCODER_CLK) && (ENCODER_CLK_prev))
    {                                      // если состояние изменилось с положительного к нулю
      if(ENCODER_DT)                        // выход В в полож. сост., значит вращение по часовой стрелке
      {
        if(fanPower - stepFen >= 0)
        fanPower -= stepFen;              // увеличиваем мощность фена, не ниже 0         
      }   
      else                                
// выход В в 0 сост., значит вращение против часовой стрелки 
      { 
        if
(fanPower + stepFen <= 255) 
        fanPower 
+= stepFen;            // уменьшаем мощность фена, но не более чем до 255           
      }   
//======================  Обновление дисплея ======================
        u8g.firstPage();  
        do 
{draw();} while( u8g.nextPage() );
//======================  Обновление дисплея ======================
    }   
  ENCODER_CLK_prev 
= ENCODER_CLK;     // сохраняем значение А для следующего цикла 
  loopTime = currentTime;
  }   
  Dimmer 
= fanPower;                    
}

// ===================================  обработчик и прерываний  ===================== 
void halfcycle() //прерывания таймера
{
  tic++; //счетчик
  if(Dimmer < tic ) D4_High; //управляем выходом
}
void detect_up() // обработка внешнего прерывания. Сработает по переднему фронту
{
  tic=0; //обнулить счетчик
  ResumeTimer1(); //запустить таймер
  attachInterrupt(0, detect_down, HIGH); //перепрограммировать прерывание на другой обработчик
}
void detect_down() // обработка внешнего прерывания. Сработает по заднему фронту
{
  StopTimer1(); //остановить таймер
  D4_Low; //логический ноль на выходы
  tic=0; //обнулить счетчик
  attachInterrupt(0, detect_up, LOW); //перепрограммировать прерывание на другой обработчик
}

Добавил регулировку оборотов вентилятора и кнопку энкодера для выбора режима регулировки - мощность или обороты.

Код

Код: Выделить всё

#include "U8glib.h"
#include "rus6x12.h"
#include <CyberLib.h> //Библиотека от Cyber-Place.ru
//===========================================================
U8GLIB_SSD1306_128X64 u8g(A4, /* data=*/ A3, /* cs=*/ A2, /* dc=*/ A1, /* reset=*/ A0); // наш дисплей  ||
//===========================================================
volatile uint8_t tic, Dimmer;
int motorPin = 6;
int motorSpeed = 75;                   // Изначальная скорость двигателя
int fanPower = 170;                    // Изначальная мощность фена
int stepFen = 5;                       // шаг изменения мощности
int stepMoto = 1;                       // шаг изменения мощности
int selectMode = 0;                    // Что изменять, скорость или мощность фена
// ===================================  энкодер   ===================== 
unsigned long currentTime;
unsigned long loopTime;
#define PIN_ENCODER_CLK 3
#define PIN_ENCODER_DT 5
#define PIN_ENCODER_SW 7 
unsigned char ENCODER_CLK;
unsigned char ENCODER_DT;
unsigned char ENCODER_CLK_prev=0;
// =================================== Кнопка энкодера   =====================
bool     button_state      = false;
bool     button_auto_state = false;
uint32_t ms_button         = 0;
uint32_t ms_auto_click     = 0;
// ===================================  дисплей   =====================
void draw(void) {
  u8g.setFont(u8g_font_courR24n);            // тонкие цифры
  u8g.setPrintPos(15, 22);
  u8g.print(fanPower);

  u8g.setPrintPos(15, 64);
  u8g.print(motorSpeed);

  u8g.setPrintPos(-2, 70);
  u8g.print("*");
   
 
// if(selectMode == 1 && (millis() & 512))
  if(selectMode == 1)
  {  
  u8g
.setPrintPos(78, 23);
  u8g.print(".");
  }
  //if(selectMode == 2 && (millis() & 512))
  if(selectMode == 2)
  {
  u8g.setPrintPos(78, 59);
  u8g.print(".");
  }
// =================================== 
  u8g.setPrintPos(81, 6);
  u8g.setFont(rus6x12);
  u8g.print("температура");

  u8g.setPrintPos(84, 43);
  u8g.print("обороты");
// =================================== 
  u8g.setFont(u8g_font_courR14r);
  u8g.setPrintPos(95, 27);
//  u8g.print(SetPower);

  u8g.setPrintPos(95, 64);
//  u8g.print(SetSpeed);
  
// ===================================    ===================== 
  u8g.drawLine(77, 0, 77, 64);      // Вертикальная линия
  u8g.drawLine(80, 32, 128, 32);    // Горизонтальная короткая линия

//  u8g.drawBitmapP(0, 0, 1, 24, Termometr);
//  u8g.drawBitmapP(-2, 47, 2, 14, Cooler);
}
 
void setup
()  {
// =================================== Диммер  ===================== 
//TCCR0B = TCCR0B & 0b11111000 | 5;
  Dimmer = fanPower;
  D4_Out;  //Настраиваем порты на выход
  D4_Low;  //установить на выходах низкий уровень сигнала
  D2_In; //настраиваем порт на вход для отслеживания прохождения сигнала через ноль
  attachInterrupt(0, detect_up, LOW); // настроить срабатывание прерывания interrupt0 на pin 2 на низкий уровень
  StartTimer1(halfcycle, 40); //время для одного разряда ШИМ
  StopTimer1(); //остановить таймер
// ===================================  энкодер   ===================== 
  pinMode(PIN_ENCODER_CLK, INPUT);
  pinMode(PIN_ENCODER_DT, INPUT);
  pinMode(PIN_ENCODER_SW, INPUT_PULLUP); //  Кнопка энкодера
  pinMode(motorPin, OUTPUT);
  currentTime = millis();
  loopTime = currentTime; 
  Serial
.begin(9600);           // для отладки
//======================  Обновление дисплея ======================
        u8g.firstPage();  
        do 
{draw();} while( u8g.nextPage() );
//======================  Обновление дисплея ======================
} 

void loop
()  
{
// =================================== Кнопка энкодера   =====================
  uint32_t ms    = millis();
  bool pin_state = digitalRead(PIN_ENCODER_SW); 
  if
( pin_state  == LOW && !button_state && ( ms - ms_button ) > 50 )// Фиксируем нажатие кнопки  
  {
    button_state      = true;
    button_auto_state = false;  
    ms_button         
= ms;
    if(selectMode == 1)
    {
      selectMode = 2;  
      Serial
.println("Press key");
    }
    else
    
{
      selectMode = 0;
    }
//======================  Обновление дисплея ======================
        u8g.firstPage();  
        do 
{draw();} while( u8g.nextPage() );
//======================  Обновление дисплея ======================
  }
  if( pin_state  == LOW && ( ms - ms_button ) > 2000 && ( ms - ms_auto_click )>500 ) // После 2000 мс нажатия кнопки каждые 500 мс фиксируем событие нажатия  
  {
    button_auto_state = true;
    ms_auto_click     = ms;
    selectMode = 1;
    Serial.println("Auto press key");    
//======================  Обновление дисплея ======================
        u8g.firstPage();  
        do 
{draw();} while( u8g.nextPage() );
//======================  Обновление дисплея ======================
  }  
  if
( pin_state == HIGH && button_state && ( ms - ms_button ) > 50  ) // Фиксируем отпускание кнопки 
  {
    button_state      = false;   
    ms_button         
= ms;
    if( !button_auto_state )
    Serial.println("No Press key");
  }
// =================================== Вал энкодера   =====================  
  currentTime = millis();
  if(currentTime >= (loopTime + 5))
  { // проверяем каждые 5мс (200 Гц)
    ENCODER_CLK = digitalRead(PIN_ENCODER_CLK);        // считываем состояние выхода А энкодера 
    ENCODER_DT = digitalRead(PIN_ENCODER_DT);         // считываем состояние выхода B энкодера    
    if((!ENCODER_CLK) && (ENCODER_CLK_prev))
    {                                      // если состояние изменилось с положительного к нулю
      if(ENCODER_DT)                        // выход В в полож. сост., значит вращение по часовой стрелке
      {
        if (selectMode == 1)  
        
{
          if(fanPower - stepFen >= 0)
          fanPower -= stepFen;              // увеличиваем мощность фена, не ниже 0    
        }
        //else
        if (selectMode == 2) 
        
{
          if(motorSpeed + stepMoto <= 255)
          motorSpeed += stepMoto;          // Прибавляем скорость   
        }   
      
}   
      else                                
// выход В в 0 сост., значит вращение против часовой стрелки 
      { 
        if 
(selectMode == 1)  
        
{
          if(fanPower + stepFen <= 255) 
          fanPower 
+= stepFen;            // уменьшаем мощность фена, но не более чем до 255    
        }
       // else
        if (selectMode == 2) 
        
{
          if(motorSpeed - stepMoto  >= 0)
          motorSpeed -= stepMoto;          // уменьшаем скорость 
        }           
      
}   
//======================  Обновление дисплея ======================
        u8g.firstPage();  
        do 
{draw();} while( u8g.nextPage() );
//======================  Обновление дисплея ======================
    }   
  ENCODER_CLK_prev 
= ENCODER_CLK;     // сохраняем значение А для следующего цикла 
  loopTime = currentTime;
  }   
  Dimmer 
= fanPower;                    
}

// ===================================  обработчик и прерываний  ===================== 
void halfcycle() //прерывания таймера
{
  tic++; //счетчик
  if(Dimmer < tic ) D4_High; //управляем выходом
}
void detect_up() // обработка внешнего прерывания. Сработает по переднему фронту
{
  tic=0; //обнулить счетчик
  ResumeTimer1(); //запустить таймер
  attachInterrupt(0, detect_down, HIGH); //перепрограммировать прерывание на другой обработчик
}
void detect_down() // обработка внешнего прерывания. Сработает по заднему фронту
{
  StopTimer1(); //остановить таймер
  D4_Low; //логический ноль на выходы
  tic=0; //обнулить счетчик
  attachInterrupt(0, detect_up, LOW); //перепрограммировать прерывание на другой обработчик

Доработанный код - убран писк двигателя. Мотор на 11 пине

Доработанный код

Код: Выделить всё

#include "U8glib.h"
#include "rus6x12.h"
#include <CyberLib.h> //Библиотека от Cyber-Place.ru
//===========================================================
U8GLIB_SSD1306_128X64 u8g(A4, /* data=*/ A3, /* cs=*/ A2, /* dc=*/ A1, /* reset=*/ A0); // наш дисплей  ||
//===========================================================
volatile uint8_t tic, Dimmer;
int motorPin = 11;
int motorSpeed = 75;                   // Изначальная скорость двигателя
int fanPower = 170;                    // Изначальная мощность фена
int stepFen = 5;                       // шаг изменения мощности
int stepMoto = 5;                       // шаг изменения мощности
int selectMode = 0;                    // Что изменять, скорость или мощность фена
// ===================================  энкодер   ===================== 
unsigned long currentTime;
unsigned long loopTime;
#define PIN_ENCODER_CLK 3
#define PIN_ENCODER_DT 5
#define PIN_ENCODER_SW 7 
unsigned char ENCODER_CLK;
unsigned char ENCODER_DT;
unsigned char ENCODER_CLK_prev=0;
// =================================== Кнопка энкодера   =====================
bool     button_state      = false;
bool     button_auto_state = false;
uint32_t ms_button         = 0;
uint32_t ms_auto_click     = 0;
// ===================================  дисплей   =====================
void draw(void) {
  u8g.setFont(u8g_font_courR24n);            // тонкие цифры
  u8g.setPrintPos(15, 22);
  u8g.print(fanPower);

  u8g.setPrintPos(15, 64);
  u8g.print(motorSpeed);

  u8g.setPrintPos(-2, 70);
  u8g.print("*");
   
 
// if(selectMode == 1 && (millis() & 512))
  if(selectMode == 1)
  {  
  u8g
.setPrintPos(78, 23);
  u8g.print(".");
  }
  //if(selectMode == 2 && (millis() & 512))
  if(selectMode == 2)
  {
  u8g.setPrintPos(78, 59);
  u8g.print(".");
  }
// =================================== 
  u8g.setPrintPos(81, 6);
  u8g.setFont(rus6x12);
  u8g.print("температура");

  u8g.setPrintPos(84, 43);
  u8g.print("обороты");
// =================================== 
  u8g.setFont(u8g_font_courR14r);
  u8g.setPrintPos(95, 27);
//  u8g.print(SetPower);

  u8g.setPrintPos(95, 64);
//  u8g.print(SetSpeed);
  
// ===================================    ===================== 
  u8g.drawLine(77, 0, 77, 64);      // Вертикальная линия
  u8g.drawLine(80, 32, 128, 32);    // Горизонтальная короткая линия

//  u8g.drawBitmapP(0, 0, 1, 24, Termometr);
//  u8g.drawBitmapP(-2, 47, 2, 14, Cooler);
}
 
void setup
()  {
// =================================== Диммер  ===================== 
//TCCR0B = TCCR0B & 0b11111000 | 5;
//  TCCR1B = TCCR1B & 0b11111000 | 5;
  TCCR2B = TCCR2B & 0b11111000 | 7;
  Dimmer = fanPower;
  D4_Out;  //Настраиваем порты на выход
  D4_Low;  //установить на выходах низкий уровень сигнала
  D2_In; //настраиваем порт на вход для отслеживания прохождения сигнала через ноль
  attachInterrupt(0, detect_up, LOW); // настроить срабатывание прерывания interrupt0 на pin 2 на низкий уровень
  StartTimer1(halfcycle, 40); //время для одного разряда ШИМ
  StopTimer1(); //остановить таймер
// ===================================  энкодер   ===================== 
  pinMode(PIN_ENCODER_CLK, INPUT);
  pinMode(PIN_ENCODER_DT, INPUT);
  pinMode(PIN_ENCODER_SW, INPUT_PULLUP); //  Кнопка энкодера
  pinMode(motorPin, OUTPUT);
  currentTime = millis();
  loopTime = currentTime; 
  Serial
.begin(9600);           // для отладки
//======================  Обновление дисплея ======================
        u8g.firstPage();  
        do 
{draw();} while( u8g.nextPage() );
//======================  Обновление дисплея ======================
} 

void loop
()  
{
   analogWrite(motorPin, motorSpeed);  // Устанавливаем первоначальную скорость
// =================================== Кнопка энкодера   =====================
  uint32_t ms    = millis();
  bool pin_state = digitalRead(PIN_ENCODER_SW); 
  if
( pin_state  == LOW && !button_state && ( ms - ms_button ) > 50 )// Фиксируем нажатие кнопки  
  {
    button_state      = true;
    button_auto_state = false;  
    ms_button         
= ms;
    if(selectMode == 1)
    {
      selectMode = 2;  
      Serial
.println("Press key");
    }
    else
    
{
      selectMode = 0;
    }
//======================  Обновление дисплея ======================
        u8g.firstPage();  
        do 
{draw();} while( u8g.nextPage() );
//======================  Обновление дисплея ======================
  }
  if( pin_state  == LOW && ( ms - ms_button ) > 2000 && ( ms - ms_auto_click )>500 ) // После 2000 мс нажатия кнопки каждые 500 мс фиксируем событие нажатия  
  {
    button_auto_state = true;
    ms_auto_click     = ms;
    selectMode = 1;
    Serial.println("Auto press key");    
//======================  Обновление дисплея ======================
        u8g.firstPage();  
        do 
{draw();} while( u8g.nextPage() );
//======================  Обновление дисплея ======================
  }  
  if
( pin_state == HIGH && button_state && ( ms - ms_button ) > 50  ) // Фиксируем отпускание кнопки 
  {
    button_state      = false;   
    ms_button         
= ms;
    if( !button_auto_state )
    Serial.println("No Press key");
  }
// =================================== Вал энкодера   =====================  
  currentTime = millis();
  if(currentTime >= (loopTime + 5))
  { // проверяем каждые 5мс (200 Гц)
    ENCODER_CLK = digitalRead(PIN_ENCODER_CLK);        // считываем состояние выхода А энкодера 
    ENCODER_DT = digitalRead(PIN_ENCODER_DT);         // считываем состояние выхода B энкодера    
    if((!ENCODER_CLK) && (ENCODER_CLK_prev))
    {                                      // если состояние изменилось с положительного к нулю
      if(ENCODER_DT)                        // выход В в полож. сост., значит вращение по часовой стрелке
      {
        if (selectMode == 1)  
        
{
          if(fanPower - stepFen >= 0)
          fanPower -= stepFen;              // увеличиваем мощность фена, не ниже 0    
        }
        //else
        if (selectMode == 2) 
        
{
          if(motorSpeed + stepMoto <= 255)
          motorSpeed += stepMoto;          // Прибавляем скорость   
        }   
      
}   
      else                                
// выход В в 0 сост., значит вращение против часовой стрелки 
      { 
        if 
(selectMode == 1)  
        
{
          if(fanPower + stepFen <= 255) 
          fanPower 
+= stepFen;            // уменьшаем мощность фена, но не более чем до 255    
        }
       // else
        if (selectMode == 2) 
        
{
          if(motorSpeed - stepMoto  >= 0)
          motorSpeed -= stepMoto;          // уменьшаем скорость 
        }           
      
}   
//======================  Обновление дисплея ======================
        u8g.firstPage();  
        do 
{draw();} while( u8g.nextPage() );
//======================  Обновление дисплея ======================
    }   
  ENCODER_CLK_prev 
= ENCODER_CLK;     // сохраняем значение А для следующего цикла 
  loopTime = currentTime;
  }   
  Dimmer 
= fanPower;                    
}

// ===================================  обработчик и прерываний  ===================== 
void halfcycle() //прерывания таймера
{
  tic++; //счетчик
  if(Dimmer < tic ) D4_High; //управляем выходом
}
void detect_up() // обработка внешнего прерывания. Сработает по переднему фронту
{
  tic=0; //обнулить счетчик
  ResumeTimer1(); //запустить таймер
  attachInterrupt(0, detect_down, HIGH); //перепрограммировать прерывание на другой обработчик
}
void detect_down() // обработка внешнего прерывания. Сработает по заднему фронту
{
  StopTimer1(); //остановить таймер
  D4_Low; //логический ноль на выходы
  tic=0; //обнулить счетчик
  attachInterrupt(0, detect_up, LOW); //перепрограммировать прерывание на другой обработчик
}
[center]i love you [s]mxIni[/s] Mysql[/center]

гость
Аватара
гость

#25 гость » 31 июля 2018, 15:18

Как успехи? работает аппарат?

Дим M
Автор темы, Администратор
Администратор
Аватара
Дим M
Автор темы, Администратор
Администратор
Сообщения: 1608
Зарегистрирован: 5 апреля 2013
С нами: 10 лет 11 месяцев

#26 Дим » 31 июля 2018, 16:48

Пока всё застопорилось - нет времени (да и желания тоже)

Дим M
Автор темы, Администратор
Администратор
Аватара
Дим M
Автор темы, Администратор
Администратор
Сообщения: 1608
Зарегистрирован: 5 апреля 2013
С нами: 10 лет 11 месяцев

#27 Дим » 11 августа 2018, 20:50

Заказанный модуль MAX6675 оказался не исправным, так что работа опять приостановлена на неопределённое время.

Дим M
Автор темы, Администратор
Администратор
Аватара
Дим M
Автор темы, Администратор
Администратор
Сообщения: 1608
Зарегистрирован: 5 апреля 2013
С нами: 10 лет 11 месяцев

#28 Дим » 12 августа 2018, 17:10

Заказал новый модуль MAX6675, так как схема с подключенным усилителем LM358, почему то перестала работать (да и работала ли вообще :grin: ). Пока заказ не доставят - соберу фен без датчика температуры. Методом тыка термопарой мультиметра (благо доставили рабочий HoldPeak HP890CN), подберу температуру для пайки. Сегодня покрасил корпус для фена, и пока он сушится занялся "рисованием" схемы. Она почти готова, за исключением модуля MAX6675 (который добавлю позже - после установки в схему фена)

схема без датчика температуры.jpg
схема без датчика температуры
схема без датчика температуры.jpg (274.23 КБ) 3398 просмотров

demented
Рядовой
Рядовой
Аватара
demented
Рядовой
Рядовой
Сообщения: 13
Зарегистрирован: 1 сентября 2018
С нами: 5 лет 6 месяцев

#29 demented » 1 сентября 2018, 8:04

Привет, я тоже буду заказывать max6675 под купон 4/5, можешь скинуть ссылку на продавца у которого заказал нерабочий усилитель. Пишут на форумах, что очень много брака продают, а в отзывах на али получают , но не проверяют.

Дим M
Автор темы, Администратор
Администратор
Аватара
Дим M
Автор темы, Администратор
Администратор
Сообщения: 1608
Зарегистрирован: 5 апреля 2013
С нами: 10 лет 11 месяцев

#30 Дим » 5 сентября 2018, 22:27

demented писал(а):Привет, я тоже буду заказывать max6675 под купон 4/5, можешь скинуть ссылку на продавца у которого заказал нерабочий усилитель. Пишут на форумах, что очень много брака продают, а в отзывах на али получают , но не проверяют.
Привет, не рабочий MAX6675 я не на Али заказывал.
[center]i love you [s]mxIni[/s] Mysql[/center]


  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «Прочая электроника»

Кто сейчас на форуме (по активности за 5 минут)

Сейчас этот раздел просматривают: 4 гостя