Глюк с энкодером
- Войдите на сайт для отправки комментариев
Добрый вечер. Сразу скажу я в коде полный 0. Кто понимает в коде помогите пожалуйста. Собрал регулятор тока на Ардуино про мини. Скетч и как всё собрать есть на одноимённом форуме, тема "Регулятор напряжения и тока на Arduino Pro Mini" Гугл в первой строке по этому запросу ссылку на форум выдаёт.
Так вот в чем проблема. При вращении энкодера значения изменяется и как только перестал крутить всё сбрасывается в 0 . Я не могу изменить значения в настройках. Если кто сталкивался с таким глюком подскажите как решить. Возможно дело не в скетче а в монтаже. Но проверял несколько раз подключение вроде как всё правельно. Если дело в железе то куда копать?
Вот скетч. На том форуме на него нареканий нет а почему у меня так не пойму.
Написал сюда потому как на том форуме помогать не горят желанием.
efine detect 3 //детектор нуля
#define triac 5 //тиристор
#include <Wire.h>
#include <LCD_1602_RUS.h> // подключаем библиотеку LCD_1602_RUS.h
#include <EEPROM.h>
LCD_1602_RUS lcd(0x3F, 16, 2);// Для подключения ЛСД экрана через I2C
#define button1 10 // 1 кнопка
#define button2 11 // 2 кнопка
#define SW 7 // энкодер SW кнопка
#define CLK 8 // энкодер CLK
#define DT 9 // энкодер DT
#define ACCELERATOR 12 //Вывод разгона (подтянут вверх, активен когда замкнут на GND, при отпускании вызывает мощность из памяти)
#define STOP 13 //Вывод стоп (подтянут вверх, активен когда замкнут на GND, имеет приоритет
// над разгоном, при отпускании и активном разгоне - переход в разгон,
// при отпускании и неактивном разгоне - переход в 0 Ватт)
double resist_ten = 18.45; //сопротивление ТЭНа 3кВт
double ACS_coeff = 0.048828125; // Коэффициент датчика ACS712 |5А - 0.024414063 | 20А - 0.048828125 | 30A - 0.073242188 |
//------------------------------------
double max_W = 3000; //максимальная мощность ТЭНа W
double step_W = 5; //шаг установки мощности W для энкодера
//------------------------------------
int preset_max = 4; //число предустановок + 1 (0) = 5
//------------------------------------
char* name_preset[] = {"РЕКТ ", "ДИСТ ", "УСТ1 ", "УСТ2 ", "УСТ3 "};
//------------------------------------
String Var;
String T1, T2;
volatile unsigned long Iism = 0; // мгновенные значения тока
volatile int cntr = 0; // счетчик в обработчике прерывания
volatile unsigned long Isumm = 0; // переменная для хранения суммы квадратов
double real_I = 0; // переменная расчета RMS тока
double real_W = 0; // рассчитанная мощность
double total_W = 0; // расход Ватт
double sqrtUsum = 0; // переменная расчете квадратного корня
double ust_W = 0; // установленная мощность
unsigned long previousMillis = 0;
unsigned long previousMillis_info = 0;
unsigned long displayMillis = 0;
unsigned long countMillis = 0;
unsigned long reg_time = 0;
byte set = 0; // переменная включения разгона
int step_ = 0; // шаг регулировки угла
int angle_W = 2250;
int angle_W_ocr = 2250;
int zero = 0;
int preset = 0;
unsigned long showTime = 0; // время обновления экрана
int buffer_ust_W[10];
unsigned long encoderMillis = 0;
int encoder_CLK = 0;
int encoder_DT = 0;
int encoder_CLK_prev = 0;
byte set_menu_I = false;
byte set_W = false;
unsigned long SW_time_press = 0;
int SW_press_time = 0;
unsigned long button1_time_press = 0;
int button1_press_time = 0;
unsigned long button2_time_press = 0;
int button2_press_time = 0;
int ACCELERATOR_press_time = 0;
unsigned long ACCELERATOR_time_press = 0;
int STOP_press_time = 0;
unsigned long STOP_time_press = 0;
unsigned long timer1 = 0;
unsigned long work_timer = 0;
unsigned long work_timer_buffer = 0;
unsigned char timerS;
unsigned char timerM;
unsigned char timerH;
int tic1;
char TimeDate[20];
byte menu_1 = true;
unsigned long work_timer_buffer2 = 0;
void setup(void) {
Serial.begin(9600); // активируем прием сообщения
pinMode(detect, INPUT); // детектор нуля
pinMode(triac, OUTPUT); // тиристор
pinMode(CLK, INPUT); // выходы энкодера
pinMode(DT, INPUT);
pinMode(SW, INPUT);
pinMode(button1, INPUT); // кнопки
pinMode(button2, INPUT);
pinMode(ACCELERATOR, INPUT); // разгон
pinMode(STOP, INPUT); // стоп
digitalWrite(SW, HIGH); // включаем подтягивающий резистор
digitalWrite(button1, HIGH); // включаем подтягивающий резистор
digitalWrite(button2, HIGH); // включаем подтягивающий резистор
digitalWrite(ACCELERATOR, HIGH); // включаем подтягивающий резистор
digitalWrite(STOP, HIGH); // включаем подтягивающий резистор
lcd.init(); // для подключения ЛСД экрана через I2C
lcd.backlight(); // для подключения ЛСД экрана через I2C
lcd.clear();
lcd.setCursor(1, 0);
lcd.print("РЕГУЛЯТОР ТОКА");
lcd.setCursor(0, 1);
lcd.print("Версия 2.2 3 кВт"); // изменения от 06.12.17
_delay_ms(2000);
lcd.clear();
for (preset = preset_max ; preset >= 0 ; preset--) // загружаем значения из памяти
{
buffer_ust_W[preset] = EEPROM_read(preset * 2);
}
preset = 0;
//===========================================================Настройка АЦП
ADMUX = (0 << REFS1) | (1 << REFS0) | (0 << MUX2) | (0 << MUX1) | (1 << MUX0); //
/* Биты 7:6 – REFS1:REFS0. Биты выбора опорного напряжения. Если мы будем менять эти биты во время преобразования,
то изменения вступят в силу только после текущего преобразования. В качестве опорного напряжения может быть выбран AVcc
(ток источника питания), AREF или внутренний 2.56В источник опорного напряжения.
Рассмотрим какие же значения можно записать в эти биты:
REFS1:REFS0
00 AREF
01 AVcc, с внешним конденсатором на AREF
10 Резерв
11 Внутренний 2.56В источник, с внешним конденсатором на AREF
Бит 5 – ADLAR. Определяет как результат преобразования запишется в регистры ADCL и ADCH.
ADLAR = 0
Биты 3:0 – MUX3:MUX0 – Биты выбора аналогового канала.
MUX3:0
0000 ADC0
0001 ADC1
0010 ADC2
0011 ADC3
0100 ADC4
0101 ADC5
0110 ADC6
0111 ADC7*/
//=======================================================================================
ADCSRA = B11101111; //Включение АЦП
/* Бит 7 – ADEN. Разрешение АЦП.
0 – АЦП выключен
1 – АЦП включен
Бит 6 – ADSC. Запуск преобразования (в режиме однократного
преобразования)
0 – преобразование завершено
1 – начать преобразование
Бит 5 – ADFR. Выбор режима работы АЦП
0 – режим однократного преобразования
1 – режим непрерывного преобразования
Бит 4 – ADIF. Флаг прерывания от АЦП. Бит устанавливается, когда преобразование закончено.
Бит 3 – ADIE. Разрешение прерывания от АЦП
0 – прерывание запрещено
1 – прерывание разрешено
Прерывание от АЦП генерируется (если разрешено) по завершении преобразования.
Биты 2:1 – ADPS2:ADPS0. Тактовая частота АЦП
ADPS2:ADPS0
000 СК/2
001 СК/2
010 СК/4
011 СК/8
100 СК/16
101 СК/32
110 СК/64
111 СК/128*/
//===========================================================
ACSR = (1 << ACD);
//------ Timer1 ---------- Таймер задержки времени открытия триака после детектирования нуля (0 триак не откроется)
TCCR1A = 0x00; //
TCCR1B = 0x00; //
TCCR1B = (0 << CS12) | (1 << CS11) | (1 << CS10); // Тактирование от CLK.
// Если нужен предделитель :
// TCCR1B |= (1<<CS10); // CLK/1 = 0,0000000625 * 1 = 0,0000000625, 0,01с / 0,0000000625 = 160000 отсчетов 1 полупериод
// TCCR1B |= (1<<CS11); // CLK/8 = 0,0000000625 * 8 = 0,0000005, 0,01с / 0,0000005 = 20000 отсчетов 1 полупериод
// TCCR1B |= (1<<CS10)|(1<<CS11); // CLK/64 = 0,0000000625 * 64 = 0,000004, 0,01с / 0,000004 = 2500 отсчетов 1 полупериод
// TCCR1B |= (1<<CS12); // CLK/256 = 0,0000000625 * 256 = 0,000016, 0,01с / 0,000016 = 625 отсчетов 1 полупериод
// TCCR2B |= (1<<CS20)|(1<<CS21)|(1<<CS22); // CLK/1024
OCR1A = 2250; // Верхняя граница счета. Диапазон от 0 до 65535.
// Частота прерываний будет = Fclk/(N*(1+OCR1A))
// где N - коэф. предделителя (1, 8, 64, 256 или 1024)
TIMSK1 = 0x00;
attachInterrupt(1, zero_crosss_int, RISING);//вызов прерывания при детектировании нуля
}
//======================================================== Прерывание по нулю
void zero_crosss_int() {
TCNT1 = 0;
OCR1A = angle_W_ocr;
zero++;
}
//======================================================== Обработка таймера по совпадению нуля
ISR (TIMER1_COMPA_vect)
{
PORTD |= (1 << PORTD5);
TCNT1 = 65535 - 200; // Импульс включения симистора 65536 - 1 - 4 мкс, 2 - 8 мкс, 3 - 12 мкс и тд
}
ISR (TIMER1_OVF_vect) { //timer1 overflow
PORTD &= ~(1 << PORTD5);
TCNT1 = OCR1A + 1;
}
//======================================================== Обработка прерывания АЦП для расчета среднеквадратичного тока
ISR(ADC_vect)
{
byte An_pin = ADCL;
byte An = ADCH;
Iism = ((An << 8) + An_pin) - 512;
Iism *= Iism;// возводим значение в квадрат
Isumm += Iism; // складываем квадраты измерений
cntr++;
}
//======================================================== Обработка таймеров // Резерв
/*ISR (TIMER2_COMPA_vect)
{
++timer1;
if (timer1 == 250)
{
// ++systimer; //общий таймер
timer1 = 0;
}
}*/
//=====================================================================================EEPROM
// чтение
int EEPROM_read(int addr) {
byte raw[2];
for (byte i = 0; i < 2; i++) raw[i] = EEPROM.read(addr + i);
int &num = (int&)raw;
return num;
}
// запись
void EEPROM_write(int addr, int num) {
byte raw[2];
(int&)raw = num;
for (byte i = 0; i < 2; i++) EEPROM.update(addr + i, raw[i]);
}
//========================================================================================
void parseParams(String inputString) { //Функция разбора принятой в Serial port строки
String bb = inputString;
if (bb.substring(0, 2) == "TW") {
T1 = bb.substring(bb.indexOf("TW", 2) + 3); //команда
ust_W = T1.toFloat(); //Выставленная мощность с Serial порта
buffer_ust_W[preset] = ust_W;
}
}
//========================================================================================
void loop(void) {
//==================================================================================
while (Serial.available()) { // Serial port, пока не конец сообщения, читаем данные и формируем строку
char ch = Serial.read();
Var += ch;
if (ch == '\n') {
parseParams(Var); // вызываем функцию разбора принятой строки
Var = "";
}
}
if (zero >= 6) { // расчет среднеквадратичного // zero - количество полупериодов для рассчета среднеквадратичного
sqrtUsum = Isumm / cntr; //
real_I = sqrt(sqrtUsum) * ACS_coeff; //
cntr = 0;
Isumm = 0;// обнуляем суммы напряжений
zero = 0;
real_W = (real_I * real_I * resist_ten);
}
//===================================================================Меню установки
if (digitalRead(STOP) == LOW ) { // считаем сколько времени активен стоп
if (millis() - STOP_time_press > 1) {
STOP_press_time++;
STOP_time_press = millis();
}
}
if ( STOP_press_time > 1000) { // если стоп более ХХХХ мс, валим мощность в ноль
STOP_press_time = 0;
buffer_ust_W[preset] = 0;
set = 3;
}
if (digitalRead(STOP) == HIGH && set == 3) { // очищаем счетчик, если стоп отпущен и перед этим был включен,
// на заданную мощность
buffer_ust_W[preset] = EEPROM_read(preset * 2);
set = 0;
}
if (digitalRead(ACCELERATOR) == LOW ) { // считаем сколько времени активен разгон
if (millis() - ACCELERATOR_time_press > 1) {
ACCELERATOR_press_time++;
ACCELERATOR_time_press = millis();
}
}
if (digitalRead(STOP) == HIGH && ACCELERATOR_press_time > 1000) { // если разгон активен более ХХХХ мс и нет стопа,
// ставим 3000 Ватт и взводим флаг разгона
ACCELERATOR_press_time = 0;
buffer_ust_W[preset] = 3000;
set = 1;
}
if (digitalRead(ACCELERATOR) == HIGH && set == 1) { // очищаем счетчик, если разгон отпущен и перед этим был включен,
// и на заданную мощность
buffer_ust_W[preset] = EEPROM_read(preset * 2);
set = 0;
}
if (digitalRead(SW) == LOW) { // считаем сколько времени нажата кнопка энкодера
if (millis() - SW_time_press > 1) {
SW_press_time++;
SW_time_press = millis();
}
}
if (digitalRead(button1) == LOW) { // считаем сколько времени нажата 1 кнопка
if (millis() - button1_time_press > 1) {
button1_press_time++;
button1_time_press = millis();
}
}
if (digitalRead(button2) == LOW) { // считаем сколько времени нажата 2 кнопка
if (millis() - button2_time_press > 1) {
button2_press_time++;
button2_time_press = millis();
}
}
if (digitalRead(SW) == HIGH) { // очищаем счетчик если кнопка энкодера отпущена
SW_press_time = 0;
}
if (digitalRead(button1) == HIGH) { // очищаем счетчик если кнопка 1 отпущена
button1_press_time = 0;
}
if (digitalRead(button2) == HIGH) { // очищаем счетчик если кнопка 2 отпущена
button2_press_time = 0;
}
if ( SW_press_time >= 300 && !set_menu_I) { // если более ХХХ мс то кнопка 0 нажата - режим отображения времени / потребленной мощности
if (!menu_1) {//
menu_1 = true;
}
else {
menu_1 = false;
}
SW_press_time = 0;
lcd.clear();
}
if ( SW_press_time >= 300 && set_menu_I) { // если более ХХХ мс то кнопка 0 нажата - запись в память
SW_press_time = 0;
EEPROM_write(preset * 2, buffer_ust_W[preset]);
lcd.setCursor(14, 1);
lcd.print("WR");
}
if ( button1_press_time >= 300) { // если более ХХХ мс то кнопка 1 нажата - режим установки
if (!set_menu_I) {//
set_menu_I = true;
}
else {
set_menu_I = false;
}
button1_press_time = 0;
lcd.clear();
}
if ( button2_press_time >= 300 && !set_menu_I) { // если более ХХХ мс и менее хх мс то кнопка 2 нажата - режим включения/выключения
if (!set_W) {//
set_W = true;
}
else {
set_W = false;
}
button2_press_time = 0;
lcd.clear();
}
//=========================================================================Энкодер
//меню установки мощности
if (set_menu_I) {
if (millis() >= (encoderMillis + 1)) { // проверяем каждые 1 мс
encoder_CLK = digitalRead(CLK); // считываем состояние выхода А энкодера
encoder_DT = digitalRead(DT); // считываем состояние выхода B энкодера
if ((!encoder_CLK) && (encoder_CLK_prev)) { // если состояние изменилось с положительного к нулю
if (encoder_DT) {
// выход DT в полож. сост., значит вращение по часовой стрелке
// увеличиваем
if (buffer_ust_W[preset] <= max_W - step_W) buffer_ust_W[preset] = buffer_ust_W[preset] + step_W;
}
else {
// выход DT в 0 сост., значит вращение против часовой стрелки
// уменьшаем
if (buffer_ust_W[preset] >= 0 + step_W) buffer_ust_W[preset] = buffer_ust_W[preset] - step_W;
}
}
encoder_CLK_prev = encoder_CLK; // сохраняем значение А для следующего цикла
encoderMillis = millis();
}
}
//крутилка пресетов
if (!set_menu_I) {
if (millis() >= (encoderMillis + 1)) { // проверяем каждые 1 мс
encoder_CLK = digitalRead(CLK); // считываем состояние выхода А энкодера
encoder_DT = digitalRead(DT); // считываем состояние выхода B энкодера
if ((!encoder_CLK) && (encoder_CLK_prev)) { // если состояние изменилось с положительного к нулю
if (encoder_DT) {
// выход DT в полож. сост., значит вращение по часовой стрелке
// увеличиваем
preset = preset + 1;
if (preset > preset_max) preset = 0;
}
else {
// выход DT в 0 сост., значит вращение против часовой стрелки
// уменьшаем
preset = preset - 1;
if (preset < 0) preset = preset_max;
}
}
encoder_CLK_prev = encoder_CLK; // сохраняем значение А для следующего цикла
encoderMillis = millis();
}
}
//==============================================регулятор
if (millis() - countMillis >= 15) { // раз в пол секунды регулируем, индицируем
countMillis = millis();
if (real_W >= (ust_W - 5) && real_W <= (ust_W + 5))
{
angle_W = angle_W;
}
if (real_W > (ust_W + 5))
{
angle_W ++;
}
if (real_W < (ust_W - 5))
{
angle_W --;
}
}
//--------------------------------------
if (angle_W >= 2250) {
angle_W = 2250;
}
if (angle_W <= 1)
{
angle_W = 1;
}
//--------------------------------------
if (ust_W < 0 || buffer_ust_W[preset] < 0) { //Если меньше 0, то 0
ust_W = 0;
buffer_ust_W[preset] = 0;
}
if (set_W) {
ust_W = (buffer_ust_W[preset] > 0) ? buffer_ust_W[preset] : 0;
TIMSK1 |= (1 << OCIE1A) | (1 << TOIE1);
}
else
{
ust_W = 0;
TIMSK1 = 0x00;
PORTD &= ~(1 << PORTD5);
}
//------------------------------------
angle_W_ocr = angle_W;
//=========================Часы работы
if (set_W) {
if (millis() - work_timer_buffer >= 1000) {
work_timer ++;
work_timer_buffer = millis();
}
}
timerH = work_timer / 3600;
tic1 = work_timer % 3600;
timerM = tic1 / 60;
timerS = tic1 % 60;
sprintf(TimeDate, "%02u:%02u:%02u", timerH, timerM, timerS);
//=======================Расход Ватт
if (work_timer - work_timer_buffer2 >= 5) // снимаем показания каждые пять секунд
{
work_timer_buffer2 = work_timer;
total_W = total_W + (real_W / 720); // и делим на 720 значений 5*720=3600 сек = 1 час. Итого средняя мощность за час из 720 измерений
}
//=========================Дисплей
if (millis() - displayMillis >= 1000) {
displayMillis = millis();
if (menu_1 && !set_menu_I) {
lcd.setCursor(0, 0);
lcd.print("ФАКТ ");
if (set==3)
lcd.print("СТОП");
else {
lcd.print(int(real_W));
lcd.print(" ");}
lcd.setCursor(9, 0);
lcd.print(" Вт");
lcd.setCursor(0, 1);
lcd.print(name_preset[preset]);
if (set==1)
lcd.print("РЗГН");
else {
lcd.print(buffer_ust_W[preset]);
lcd.print(" ");}
lcd.setCursor(9, 1);
lcd.print(" Вт");
}
if (!menu_1 && !set_menu_I) {
lcd.setCursor(0, 0);
lcd.print("BPM ");
lcd.print(TimeDate);
lcd.setCursor(0, 1);
lcd.print("Вт*ч ");
lcd.print(total_W);
}
if (set_menu_I) {
lcd.setCursor(0, 0);
lcd.print("УСТАНОВКА:");
lcd.setCursor(0, 1);
lcd.print(name_preset[preset]);
lcd.print(">>");
lcd.print(buffer_ust_W[preset]);
lcd.print(" ");
}
if (set_W) {
lcd.setCursor(13, 0);
lcd.print("ВКЛ");
}
else {
lcd.setCursor(12, 0);
lcd.print("ВЫКЛ");
}
}
//=========================Информация в порт раз в секунду
if (millis() - previousMillis_info >= 1000) {
previousMillis_info = millis();
Serial.print("realW "); //Измеренная мощность
Serial.println(real_W);
Serial.print("ustW "); //Установленная мощность
Serial.println(ust_W);
Serial.print("angleW "); //Угол установленный
Serial.println(angle_W);
// Serial.print("step "); //Угол установленный
// Serial.println(step_);
Serial.println("-----------------");
}
//========================================================
}
[quote]
При вращении энкодера значения изменяется и как только перестал крутить всё сбрасывается в 0.
[\quote]
вы о каком то конкретном значении или обо все вообще ?
можете более подробно описать что вы видите когда на lcd экране устройства "При вращении энкодера значения изменяется и как только перестал крутить всё сбрасывается в 0."
в каком состоянии тэн ? включен,выключен,нагревается,остывает ?
Нет не о конкретном. Все изменяемые значения.
Например в скетче есть 4 предустановки (char* name_preset[] = {"РЕКТ ", "ДИСТ ", "УСТ1 ", "УСТ2 ", "УСТ3 "};) все они при первом включении на 0. Я выбираю любую начинаю крутить энкодер значения меняются по 5 единиц как и задано в скетче (double step_W = 5; //шаг установки мощности W для энкодера) могу накрутить и 80 на ЛСД значения изменяются но как только перестаю крутить сбрасывается в 0. И так любое значение которое можно изменять.
[/quote]в каком состоянии тэн ? включен,выключен,нагревается,остывает ? [/quote]
Тен я пока не подключал но пробовал и с включенным симистором и нет. Да и задовать предустановки можно перед включением нагрузки.
Вот описание от автора:
Программа имеет три меню:
1. Текущая мощность (ВАТТ), включен или выключен регулятор (OFF/ON)
Название предустановки, установленная мощность.
2. Время работы, включен или выключен регулятор (OFF/ON)
Потребленная мощность в ваттах.
3. Установка, включен или выключен регулятор (OFF/ON)
Название предустановки >> устанавливаемая мощность.
Переключение между меню 1 и 2 с помощью кнопки энкодера;
Переключение между предустановками с помощью энкодера;
Переход в меню 3 и выход из него нажатием кнопки 1 , установка мощности с помощью энкодера;
Запись в память установки с помощью кнопки энкодера в меню 3, в качестве подтверждения появится надпись WR;
Включение регулятора кнопкой 2 в меню 1 или 2. Если требуется регулировка на «ходу» то при включенном регуляторе нажать кнопку 1 и производить регулировку из меню 3 установки;
емкости между выводами энкодера и выводом GND есть?
если нет, ставьте по 0,1 мкф потом смотрите
У меня шилд и раньше нет не так, другой скетч от другого автора работает без нареканий. Я бы им и пользовался но в нём нет некоторых функций которые есть в этом.
если смотреть на строчки : 303 - 314 : если STOP активен ,то вбранный данный пресет обнулять
STOP ,в вашем случае, пин 13 ,активен когда подтянут к 0 :
убедитесь что написанное соотвествует дейстивтельности.
далее сточках 496-498 тоже есть обнуление.
ради эксперимента в строчке 312 напишите
а в строчке 498 :
и попробуйте повторить ваши дейстивя с крутилками
Окей спасибо попробую, не знаю успею ли сегодня но как попробую отпишусь.
ну там код, прямо скажем, очень и очень кривой. При вращении энкодера данные с него считываются 1000 раз в сек, а значение на экране обновляется лишь раз в секунду. При этом код энкодера написан так, что при его "перекручивании" значния сбрасываются на ноль.
Попробуйте крутить ручку ОЧЕНЬ медленно, чтобы увеличение уставок происходило раз в 5-10 секунд, должно помочь.
ну там код, прямо скажем, очень и очень кривой. При вращении энкодера данные с него считываются 1000 раз в сек, а значение на экране обновляется лишь раз в секунду. При этом код энкодера написан так, что при его "перекручивании" значния сбрасываются на ноль.
Попробуйте крутить ручку ОЧЕНЬ медленно, чтобы увеличение уставок происходило раз в 5-10 секунд, должно помочь.
Не помогло.
далее сточках 496-498 тоже есть обнуление.
Спасибо вот кажись оно:
if (ust_W < 0 || buffer_ust_W[preset] < 0) { //Если меньше 0, то 0
ust_W = 0;