Эх сурвалжийг харах

Move USART functions to separate module.

Vladimir N. Shilov 1 жил өмнө
parent
commit
d1ef638643
4 өөрчлөгдсөн 402 нэмэгдсэн , 386 устгасан
  1. 28 360
      lib/stab/stab.c
  2. 7 26
      lib/stab/stab_param.h
  3. 334 0
      lib/stab/usart.c
  4. 33 0
      lib/stab/usart.h

+ 28 - 360
lib/stab/stab.c

@@ -1,6 +1,7 @@
 #include "ch.h"
 #include "hal.h"
 #include "stab_param.h"
+//#include "usart.h"
 
 /* Версия скетча и длина версии скетча в символах для правильного вывода на дисплей */
 #define VERSION "v0.100"
@@ -47,9 +48,11 @@ static volatile struct flags {  // Флаги
   unsigned  stab_off : 1;     // Флаг аварийного останова стабилизатора
   unsigned  butt : 1;         // Флаг опроса кнопок
   unsigned  writable : 1;     // Флаг записи уставок в EEPROM
+#ifdef USE_USART  
   unsigned  uartUnhold : 1;   // Флаг разрешения передачи данных по USART
   unsigned  uartReport : 1;   // Флаг разрешения отправки данных внешнему контроллеру
   unsigned  uartTimeout : 1;  // Флаг истечения времени приема посылки по USART
+#endif
 } fl = {};  /* Инициализируем структуру с нулевыми членами */
 
 //static uint8_t fl_A;  // Байт флажков A
@@ -71,11 +74,11 @@ static uint8_t cnt_PDMcount;    // Счетчик для перебора уст
 static uint8_t cnt_menuWDT;     // Счетчик секунд для организации отсчета ожидания выхода из меню
 static uint8_t cnt_dspMenu;     // Индикатор режима меню
 
-uint8_t X_position (const uint8_t x, const uint16_t arg = 0, const uint8_t pix = 6); // Функция возвращает начальную позицию по Х для десятичного числа, в зависимости от количества знаков в нём.
-uint8_t X_centred (const uint8_t len); // Функция возвращает начальную позицию по Х для текста длинной len знаков, для размещения оного по центру дисплея.
-uint8_t A_to_HEX (const char a); // Функция переводит символ ASCII в шестнадцатиричную цифру
-char HEX_to_A (const uint8_t x); // Функция переводит шестнадцатиричную цифру в символ ASCII
-uint16_t calc_proportion(const uint16_t multiplier1, const uint16_t multiplier2 = Pnom, const uint32_t divider = CICLE);
+static uint8_t X_position (const uint8_t x, const uint16_t arg = 0, const uint8_t pix = 6); // Функция возвращает начальную позицию по Х для десятичного числа, в зависимости от количества знаков в нём.
+static uint8_t X_centred (const uint8_t len); // Функция возвращает начальную позицию по Х для текста длинной len знаков, для размещения оного по центру дисплея.
+static uint8_t A_to_HEX (const char a); // Функция переводит символ ASCII в шестнадцатиричную цифру
+static char HEX_to_A (const uint8_t x); // Функция переводит шестнадцатиричную цифру в символ ASCII
+static uint16_t calc_proportion(const uint16_t multiplier1, const uint16_t multiplier2 = Pnom, const uint32_t divider = CICLE);
 
 /*
  * ПРОЦЕДУРЫ И ФУНКЦИИ
@@ -88,7 +91,7 @@ uint16_t calc_proportion(const uint16_t multiplier1, const uint16_t multiplier2
  * @param х позиция для arg, если бы оно было однозначно;
  * @param pix ширина шрифта в пикселях.
  */
-uint8_t X_position (const uint8_t x, const uint16_t arg, const uint8_t pix) {
+static uint8_t X_position (const uint8_t x, const uint16_t arg, const uint8_t pix) {
   //uint8_t pix = 6; // Ширина шрифта в пикселях
   if (arg < 10) {
     return pix * x;
@@ -106,7 +109,7 @@ uint8_t X_position (const uint8_t x, const uint16_t arg, const uint8_t pix) {
  * для размещения оного по центру дисплея.
  * @param len Количество знакомест в тексте
  */
-uint8_t X_centred (const uint8_t len) {
+static uint8_t X_centred (const uint8_t len) {
   uint8_t wdt = 128; // Ширина дисплея в пикселях
   uint8_t pix = 6;   // Ширина шрифта в пикселях
   if (len > wdt/pix) {
@@ -121,7 +124,7 @@ uint8_t X_centred (const uint8_t len) {
  * @return при ошибке возвращает 255
  * @param a символ 0...F
  */
-uint8_t A_to_HEX (const char a) {
+static uint8_t A_to_HEX (const char a) {
   if (a >= 48 && a <= 57) { // Если а - от 0 до 9
     return (uint8_t)(a-48);
   } else if (a >= 65 && a <= 70) { // Если а - от A до F
@@ -138,7 +141,7 @@ uint8_t A_to_HEX (const char a) {
  * @return при ошибке возвращает X
  * @param x число, кое необходимо перевести в ASCII-код
  */
-char HEX_to_A (const uint8_t x) {
+static char HEX_to_A (const uint8_t x) {
   if (x <= 9) {
     return (char)(x + 48);
   } else if (x <= 15) {
@@ -151,7 +154,7 @@ char HEX_to_A (const uint8_t x) {
 /**
  * @brief Подпрограммка остановки режима "Разгон"
  */
-void stop_razgon(void) {
+static void stop_razgon(void) {
   fl.razg_on = 0; //выключим режим разгона
   fl.TRelay  = 0; //выключим контактное реле
 }
@@ -159,7 +162,7 @@ void stop_razgon(void) {
 /**
  * @brief Подпрограммка подсчета Pust
  */
-void set_Pust(void) {
+static void set_Pust(void) {
   Pust = calc_proportion(PDMust);
 }
 
@@ -172,7 +175,7 @@ void set_Pust(void) {
  * @param multiplier2 второй множитель (по умолчанию Pnom),
  * @param divider делитель (по умолчанию CICLE).
  */
-uint16_t calc_proportion(const uint16_t multiplier1, const uint16_t multiplier2, const uint32_t divider) {
+static uint16_t calc_proportion(const uint16_t multiplier1, const uint16_t multiplier2, const uint32_t divider) {
   uint32_t p;
   p = (long)multiplier1 * 2;
   p *= (long)multiplier2;
@@ -185,7 +188,7 @@ uint16_t calc_proportion(const uint16_t multiplier1, const uint16_t multiplier2,
 /**
  * @brief Пауза, измеряется в полупериодах
  */
-void pp_Delay(const uint16_t pp) {
+static void pp_Delay(const uint16_t pp) {
   uint16_t PPcount = 0; // счетчик полупериодов
   bool PP_tm_last = 0;
   while (PPcount < pp) {
@@ -200,346 +203,12 @@ void pp_Delay(const uint16_t pp) {
   }
 }
 
-#ifdef USE_USART
-/**
- * @brief USART initialization
- * Если задействовано управление регулятором ТЭНа через UART, инициализируем оный
- */
-void USART_start(void) {
-  Serial.begin(9600, SERIAL_8N1); // Инициализируем USART
-#ifdef Debug
-  Serial.println("Started");
-#endif
-}
-
-/**
- * @brief Функция возвращает значение текущей мощности с учетом режима "Разгон"
- */
-uint16_t get_Power(void) { 
-  if (fl.stab_off || fl.Udown || fl.NotZero) {  // Если авария или сеть в дауне - передаем ноль
-    return 0;
-  }
-  else if (fl.razg_on || fl.Ulow) { // В разгоне и при недостаточном сетевом передаем расчетную текущую мощность
-    uint32_t tmp_u = (long)U_sum * U_sum;
-    tmp_u /= 100;
-    tmp_u *= Pnom;
-    tmp_u /= U_LINE_Q;
-    return tmp_u;
-  } else { // В рабочем режиме - передаем уставку
-    return Pust;
-  }
-}
-
-/**
- * @brief Функция установки текущей мощности по запросу
- */
-void set_newPDM(uint16_t power) {
-  if (power >= Pnom) { // Если параметр превышает максимально возможное значение,
-    PDMust = CICLE; // значит ставим максимально возможное значение.
-  } else {
-    PDMust = calc_proportion(power, CICLE, Pnom);
-  }
-
-  set_Pust(); // Пересчитаем Pust
-  fl.dspNewData = 1; // Обновление информации на дисплее
-}
-
-#endif /* USE_USART */
-
-#ifdef USE_ADprotocol
-/* USART */
-
-/**
- * Байт "состав данных" b00010111 (основной параметр - мощность в нагрузке,
- * доп. параметр - напряжение сети) в HEX-формате 0x17
- */
-static char USART_InfoData[14] = {'T','1','7','0','0','0','0','0','0','0','0','0','0',0x0D};  // Массив готовых данных для передачи внешнему контроллеру
-static char USART_SetData[6];  // Массив управляющих символов от внешнего контроллера
-
-/**
- * @brief Парсим управляющую последовательность по универсальному протоколу
- */
-void USART_parser(void) {
-  static uint8_t index = 0;
-  static uint8_t data_size;
-
-  while (Serial.available() > 0) {
-    if (fl.stab_off) {
-      Serial.read(); // Вычитываем очередной байт, чтобы не засирать буфер
-    } else if ( !index || fl.uartTimeout ) {   // Начало
-      USART_SetData[0] = Serial.read(); // Вычитываем очередной байт
-      fl.uartTimeout = 0;               // Сбросим флаг таймаута ожидания окончания посылки
-      cnt_uartWDT = 0;                  // Сбросим таймер ожидания окончания посылки
-      switch ( USART_SetData[0] ) {     // Ждём первый символ...
-        case 'M':
-        case 'm': {                     // ...запроса на изменение режима работы
-          data_size = 2;
-          index=1;
-          break;
-        }
-        case 'P':
-        case 'p': {                     // ...запроса на изменение уставки
-          data_size = 5;
-          index=1;
-          break;
-        }
-        default: {
-          // break;
-        }
-      }
-    } else {
-      USART_SetData[index] = Serial.read(); // Вычитываем очередной байт
-      if ( USART_SetData[index] == 0x0D ) { // Ждем последнего символа посылки <CR>
-        if ( index == data_size ) {
-          switch (index) {
-            case 2: { // Парсим запрос на смену режима
-              switch ( USART_SetData[1] ) {
-                case '0': { // Переход в рабочий режим
-                  if ( fl.razg_on ) {
-                    stop_razgon();
-                  }
-                  break;
-                }
-                case '1': { // Переход в режим разгона
-                  if ((!fl.NotZero) & (!fl.Udown) & (!fl.razg_off)) {  // Если электросеть в дауне или разгон запрещен - не разгонишься
-                    fl.razg_on = 1;
-                    fl.razg = 1;
-                  }
-                  break;
-                }
-                case '2': { // Отключение нагрузки
-                  PDMust = 0;
-                  stop_razgon();
-                  Pust = 0;
-                  break;
-                }
-                default: {
-                  break;
-                }
-              }
-              break;
-            }
-            case 5: { // Парсим запрос на смену уставки
-              uint16_t tmp_p = 0;
-              uint8_t b;
-              for (uint8_t x=1; x <= 4; x++ ) {
-                tmp_p *= 16;
-                b = A_to_HEX (USART_SetData[x]);
-                if (b == 255) {
-                  break;
-                }
-                tmp_p += b;
-              }
-              if (b != 255) {
-                set_newPDM (tmp_p);   // Установим новую уставку мощности;
-              }
-              break;
-            }
-          }
-          index = 0;
-          fl.dspNewData = 1;  //Обновление информации на дисплее
-        } else index = 0;
-      } else if ( index++ == data_size ) {
-        index = 0;
-      }
-    }
-  }
-}
-
-/**
- * @brief Отчет внешнему контроллеру по универсальному протоколу
- */
-void USART_report(void) {
-  uint16_t b;
-
-  if (fl.stab_off) {
-    b = 3;  // b000000(11) - аварийное отключение нагрузки (удаленное включение невозможно)
-  } else if (fl.Udown || fl.NotZero) {
-    b = 6;  // b000001(10) - отсутствие сетевого напряжения, нагрузка отключена
-  } else if (fl.razg_on) {
-    b = 1;  // b(000000)(01) - разгон
-  } else if (PDMust == 0) {
-    b = 2;  // b000000(10) - нагрузка отключена
-  } else if (fl.Ulow) {
-    b = 8;   // b000010(00) - напряжения сети недостаточно для достижения уставки
-  } else {
-    b = 0;  // b000000(00) - режим рабочий, ошибок нет
-  }
-
-  // Закодируем состав данных
-  USART_InfoData[3] = HEX_to_A ( b / 16 );  // Старший разряд байта "Режим + ошибки"
-  USART_InfoData[4] = HEX_to_A ( b % 16 );  // Младший разряд байта "Режим + ошибки"
-
-  // Закодируем основной параметр - мощность на выходе
-  b = get_Power();  // Получим текущую мощщу
-  USART_InfoData[8] = HEX_to_A ( b % 16 );  // 0 разряд основного параметра
-  b /= 16;
-  USART_InfoData[7] = HEX_to_A ( b % 16 );  // 1 разряд основного параметра
-  b /= 16;
-  USART_InfoData[6] = HEX_to_A ( b % 16 );  // 2 разряд основного параметра
-  USART_InfoData[5] = HEX_to_A ( b / 16 );  // 3 разряд основного параметра
-
-  // Закодируем доп.параметр - напряжение сети
-  if (fl.NotZero) { // Если сети нет, то и на выходе пусто
-    USART_InfoData[12] = '0'; // 0 разряд основного параметра
-    USART_InfoData[11] = '0'; // 1 разряд основного параметра
-    USART_InfoData[10] = '0'; // 2 разряд основного параметра
-    USART_InfoData[9]  = '0'; // 3 разряд основного параметра
-  } else {
-    b = U_sum;
-    USART_InfoData[12] = HEX_to_A ( b % 16 ); // 0 разряд основного параметра
-    b /= 16;
-    USART_InfoData[11] = HEX_to_A ( b % 16 ); // 1 разряд основного параметра
-    b /= 16;
-    USART_InfoData[10] = HEX_to_A ( b % 16 ); // 2 разряд основного параметра
-    USART_InfoData[9]  = HEX_to_A ( b / 16 ); // 3 разряд основного параметра
-  }
-  // Отправим
-  Serial.write(USART_InfoData, 14);
-}
-#endif /* USART */
-
-#ifdef USE_RMVK
-/* RMVK_/_Samovar */
-
-/**
- * @brief Функция возвращает значение текущего напряжения без десятичного знака
- */
-uint16_t get_Uin(void) {
-  return ((U_real_dec < 5)? U_real : (U_real + 1));
-}
-
-/**
- * @brief Функция возвращает расчетное значение текущего (если getReal=true)
- * или желаемого (если getReal=false) напряжения
- */
-uint16_t get_Uout(const bool getReal) {
-  if ( fl.Udown || fl.NotZero || (PDMust == 0) ) {    // Если сеть в дауне или стаб в стопе - передаем ноль
-    return 0;
-  } else if ( getReal && ( fl.razg_on || fl.Ulow ) ) {  // В разгоне и при недостаточном сетевом передаем текущее сетевое, если надо
-    return get_Uin();
-  } else {    // В рабочем режиме - передаем уставку
-    return calc_proportion(PDMust, U_LINE);
-  }
-}
-
-/**
- * @brief Парсим управляющую последовательность от RMVK_/_Samovar
- */
-void USART_parser(void) {
-  static String inoutString;
-  static uint8_t index = 0;
-
-  while (Serial.available() > 0) {
-    char inChar = (char)Serial.read();// Вычитываем очередной байт
-    if ( !index || fl.uartTimeout ) { // Начало посылки
-      if ((inChar == 'A') || ((uint8_t)(inChar) == 0xD0)) { // Ждём первый символ посылки "A" или первый байт UTF-кириллицы из протокола Samovar'a
-        inoutString = inChar;
-        index=1;
-        fl.uartTimeout = 0; // Сбросим флаг таймаута ожидания окончания посылки
-        cnt_uartWDT = 0;    // Сбросим таймер ожидания окончания посылки
-      }
-    } else if ( index++ < 13 ) {  // Пока посылка не длиннее 13 символов, считаем её корректной
-      if ( inChar == 0x0D ) {   // Ждем последнего символа посылки <CR>
-        index = 0;
-        // Парсим строку, поскольку кончилась
-        // В протоколе Samovar стандартное начало посылки "АТ" пересылается русскими символами в Юникоде. Баг или фича?
-        if (( inoutString == ("AT+VI?")) ||  // Запрос текущего напряжения сети
-            ( inoutString == ("АТ+VI?"))) {  // В этой строке "АТ" - русскими символами!
-          if (fl.NotZero) { // Если сети нет, то и на выходе пусто
-            inoutString = String(0);
-          } else {
-            inoutString = String(get_Uin());
-          }
-        } else if (( inoutString == F("АТ+VO?")) || ( inoutString == F("АТ+VS?"))) {  // Запрос текущей мощности от Samovar. В этой строке "АТ" - русскими символами!
-          inoutString = String(get_Power());
-        } else if ( inoutString == F("AT+VO?") ) {  // Запрос текущего напряжения на выходе от РМВ-К
-          inoutString = String(get_Uout(true));
-        } else if ( inoutString == F("AT+VS?") ) {  // Запрос напряжения уставки на выходе от РМВ-К
-          inoutString = String(get_Uout(false));
-        } else if ( inoutString == F("AT+ON?") ) {  // Запрос состояния выхода от РМВ-К
-          if ((PDMust == 0) || (fl.NotZero) || (fl.Udown)) {  // Если на выходе 0
-            inoutString = String("OFF");
-          } else {
-            inoutString = String("ON");
-          }
-        } else if (( inoutString == F("AT+SS?")) ||   // Запрос режима от Samovar
-                 ( inoutString == F("АТ+SS?"))) {    // В этой строке "АТ" - русскими символами!
-          if (fl.stab_off || fl.Udown || fl.NotZero) {  // При аварии, сильно пониженном напряжении сети или его отсутствии - передаем ошибку
-            inoutString = String(3);
-          } else if (fl.razg_on) {            // Передаем "Разгон"
-            inoutString = String(1);
-          } else if (PDMust == 0) {           // Передаем "Стоп"
-            inoutString = String(2);
-          } else {                            // Передаем "Рабочий режим"
-            inoutString = String(0);
-          }
-        } else if (( inoutString == F("AT+ON=0")) ||  // Запрос на выключение стабилизатора
-                 ( inoutString == F("АТ+ON=0"))) {   // В этой строке "АТ" - русскими символами!
-          if (!fl.stab_off) {  // Если стаб не выключен аварийно...
-            PDMust = 0;
-            stop_razgon();
-            Pust = 0;
-            fl.dspNewData = 1;  //Обновление информации на дисплее
-            inoutString = "";
-          }
-        } else if (( inoutString == F("AT+ON=1")) ||  // Запрос на включение режима "Разгон"
-                 ( inoutString == F("АТ+ON=1"))) {   // В этой строке "АТ" - русскими символами!
-          if ((!fl.stab_off) && (!fl.NotZero) && (!fl.Udown) && (!fl.razg_off)) {  // Если авария, электросеть в дауне или разгон запрещен - не разгонишься
-            fl.razg_on = 1;
-            fl.razg = 1;
-            fl.dspNewData = 1;  //Обновление информации на дисплее
-          }
-          inoutString = "";
-        } else if ( inoutString.substring(0,8) == F("АТ+VS=") ) {  // Запрос на изменение уставки от Samovar. В этой строке "АТ" - русскими символами!
-          if (!fl.stab_off) {  // Если стаб не выключен аварийно...
-            //выключаем разгон, на всякий случай
-            stop_razgon();
-            set_newPDM (inoutString.substring(8).toInt());        // Установим новую уставку мощности
-            inoutString = "";
-          }
-        } else if ( inoutString.substring(0,6) == F("AT+VS=") ) { // Запрос на изменение уставки от РМВ-К
-          if (fl.stab_off || fl.Udown || fl.NotZero) {  // Если авария или сеть в дауне - ничего не меняем, передаем ошибку
-            inoutString = String(F("error"));
-          } else {
-            uint16_t tmp_u = inoutString.substring(6).toInt();
-            if ( tmp_u < U_LINE ) {
-              tmp_u *= CICLE;
-              PDMust = tmp_u / U_LINE;
-            } else PDMust = CICLE;
-            //выключаем разгон, на всякий случай
-            stop_razgon();
-            set_Pust();         // Посчитаем Pust
-            fl.dspNewData = 1;  // Обновление информации на дисплее
-            inoutString = String(get_Uout(false));
-          }
-        } else {  // Неизвестная или закосяченная команда
-#ifdef Debug
-          inoutString = String(F("(o_O unknown!)"));
-#else
-          inoutString = "";
-#endif
-        }
-        if ( inoutString != "" )  {       // Если строка не пустая
-          inoutString += char(0x0D);    // Добавляем в конец <CR>
-          Serial.print( inoutString );  // Шлём!
-        }
-      } else {  // Еще не конец
-        inoutString += inChar;  // Добавляем и это лыко в строку
-      }
-    } else {  // Посылка длинновата, а значит - некорректна, начинаем сначала
-      index = 0;
-    }
-  }
-}
-#endif /* RMVK_/_Samovar */
-
 /**
  * @brief Подпрограмма запоминания последней уставки
  * Проверяет последнюю уставку на совпадение с уже записанными
  * в массив уставок и запоминает, если надо
  */
-void remember_last_power_setting(void) { // Запомним последнюю уставку
+static void remember_last_power_setting(void) { // Запомним последнюю уставку
   bool isnew = 1;
   for (int8_t x = PDMset_ARR_SIZE - 1; x >= 0; x--) { // Проверим новое значение на совпадение с уже записанными
     if (PDMust == PDMset[0][x]) {
@@ -561,18 +230,16 @@ void remember_last_power_setting(void) { // Запомним последнюю
  * @param index1 индех второго измерения первой обмениваемой ячейки,
  * @param index2 индех второго измерения второй обмениваемой ячейки.
  */
-void change_arr_cell(uint16_t arr[2][ARRAY_SIZE], const uint8_t index, const uint8_t index1, const uint8_t index2) {
+static void change_arr_cell(uint16_t arr[2][ARRAY_SIZE], const uint8_t index, const uint8_t index1, const uint8_t index2) {
   uint16_t k = arr[index][index1];
   arr[index][index1] = arr[index][index2];// Обмениваемся
   arr[index][index2] = k;
 }
 
-
-
 /**
  * @brief Инициализация АЦП
  */
-void ADC_init(void) {
+static void ADC_init(void) {
   ADMUX = 0;
   ADMUX |= ( 1 << REFS0); // Задаем ИОН равный напряжению питания
   ADMUX |= 0;             // Выбираем пин A0 для преобразования
@@ -584,7 +251,7 @@ void ADC_init(void) {
 /**
  * @brief Инициализация таймеров
  */
-void Timers_init(void) {
+static void Timers_init(void) {
   //---Инициализация таймера 0 для тактирования АЦП -------------
   TCCR0A = 0;
   TCCR0B = 0;
@@ -604,7 +271,7 @@ void Timers_init(void) {
 /**
  * @brief Инициализация входов/выходов контроллера
  */
-void Pins_init(void) {
+static void Pins_init(void) {
 #ifdef DisplayReset
   pin_OLEDres_INIT;
   pin_OLEDres_LOW;  // Сбрасываем дисплей (!!! НЕ ЗАБЫТЬ ПЕРЕКЛЮЧИТЬ НА ВЫСОКИЙ !!!) TODO!
@@ -630,7 +297,7 @@ void Pins_init(void) {
  * симистором твердотельного
  * в момент включения/выключения режима "Разгон"
  */
-void Razgon_(void) {
+static void Razgon_(void) {
 #define RELAY_SHUNTING_TIME 50  // количество полупериодов, в течение которых шунтируются контакты реле
   static uint8_t cnt_P_relay=0; // Счетчик полупериодов шунтирования контактного реле
   if (fl.razg_on &&             // Если включен разгон..
@@ -650,7 +317,7 @@ void Razgon_(void) {
 /**
  * @brief Подпрограмма управления твердотельным реле ТЭНа
  */
-void PDM_(void) {
+static void PDM_(void) {
   if (fl.razg) {
     pdm = CICLE; // В режиме разгона твердотельное всегда открыто
   }
@@ -679,7 +346,7 @@ void PDM_(void) {
 /**
  * @brief Опрос кнопок
  */
-void Buttons_(void) {
+static void Buttons_(void) {
   static uint8_t butt = 0; // код текущей нажатой кнопки
   static uint8_t last_butt = 0;  // код предыдущей нажатой кнопки
   static struct buttons {
@@ -730,7 +397,9 @@ void Buttons_(void) {
               Pnom = PDMset[0][0]; // По умолчанию установим номинальную мощность из нулевой ячейки
               fl.writable = 1;     // Уставки пишутся в EERPOM
               //EEPROM_read_PDMs();  // Читаем уставки
+#ifdef USE_USART              
               fl.uartUnhold = 1;   // Разрешим обращение к USART
+#endif              
               cnt_dspMenu = 0;     // Выйдем из менюшки
               fl.dspRefresh = 1;   // Ставим флаг обновления экрана
             }
@@ -1121,7 +790,7 @@ ISR(ADC_vect) {
 /**
  * @brief Подпрограмма обновления меню
  */
-void RefreshMenu (void) {
+static void RefreshMenu (void) {
   ASOled.clearDisplay();
   ASOled.printString_6x8(F("Ст Принять и записать"), 0, 6);
   ASOled.printString_6x8(F("Рз Принять без записи"), 0, 7);
@@ -1174,8 +843,7 @@ void RefreshMenu (void) {
  * @brief Подпрограмма печати строки минусов
  * @param str - номер строки, куда печатать минуса
  */
-*
-void Asoled_printstring_6x8_minus (const uint8_t str) {
+static void Asoled_printstring_6x8_minus (const uint8_t str) {
   ASOled.printString_6x8(F("---------------------"), 0, str);
 }
 

+ 7 - 26
lib/stab/stab_param.h

@@ -21,15 +21,7 @@
 //
 #define MENU_TIMEOUT 120  // Таймаут выхода из меню в секундах (не более 255)
 //
-//=====Настройки коммуникации по последовательному порту============
-#define USE_USART           // Раскомментить для инициализации общения стаба с внешним контроллером
-  #ifdef USE_USART
-  //#define USE_RMVK        // Раскомментить для включения общения с внешним контроллером по протоколу Samovar и/или РМВ-К
-  #ifndef USE_RMVK
-    #define USE_ADprotocol  // По умолчанию используется универсальный протокол
-  #endif
-#endif
-//==================================================================
+
 //==================================================================
 //
 #ifdef U_LINE_FREQ_60
@@ -126,7 +118,7 @@
 #define pin_LED_INIT pin_PB_OutLOW(pin_LED)       // Определяем вывод, как вЫход и устанавливаем низкий уровень
 #define TURN_LED_ON  PORTB |=(1 << (pin_LED - 8)) // Включаем светодиод
 #define TURN_LED_OFF PORTB &=~(1 << (pin_LED - 8))// Выключаем светодиод
-//
+
 //==============управление релюшками=============
 #ifdef High_level_triac // управление твердотельным реле высоким уровнем
 #define TURN_SSR_ON pin_TOut_HIGH   // Включаем ТЭН
@@ -135,7 +127,7 @@
 #define TURN_SSR_ON pin_TOut_LOW    // Включаем ТЭН
 #define TURN_SSR_OFF pin_TOut_HIGH  // Выключаем ТЭН
 #endif
-//
+
 #ifdef High_level_relay // управление контактным реле высоким уровнем
 #define TURN_RELAY_ON pin_TRelay_HIGH   // Включаем ТЭН
 #define TURN_RELAY_OFF pin_TRelay_LOW   // Выключаем ТЭН
@@ -143,7 +135,7 @@
 #define TURN_RELAY_ON pin_TRelay_LOW    // Выключаем ТЭН
 #define TURN_RELAY_OFF pin_TRelay_HIGH  // Включаем ТЭН
 #endif
-//
+
 //========коэффициенты для ПИД-регулировки=======
 #define Kp 2      // Коэффициент пропорциональности для ПИД-подстройки ФЧ сети (степень двойки для регистрового сдвига)
 #define Ki 5      // Интегральный коэффициент для ПИД-подстройки ФЧ сети (степень двойки для регистрового сдвига)
@@ -152,15 +144,15 @@
 #define PHASE 8   // сдвиг фаз между детекцией ноля и прерыванием таймера (в тиках таймера)
 #define T_MAX 180 // ограничение максимальной длительности полупериода в тиках таймера
 #define T_MIN 100 // ограничение минимальной длительности полупериода в тиках таймера
-//
+
 //================прочие константы===============
 #define ZSUM_MAX 5000 // Количество отсчетов АЦП без детекции ноля (5000 отсчетов это порядка 1 сек)
 #define U_ZERO 512    // Значение нуля АЦП для двуполярного сигнала с постоянной составляющей на выходе ОУ
 #define T_ADC 49    // Определяет интервал между запусками АЦП (200 мкс) f_OCn = f_clk / 2*N*(1 + X), где N - коэффициент деления предделителя, X- содержимое регистра OCRnA
 #define CICLE 500   // Количество полупериодов в полном цикле регулирования (200 полупериодов - 2сек, 500 полупериодов - 5сек, больше ставить не надо)
-//
+
 #define ASOled LD // Заюзаем уже созданный в библиотеке дисплея объект LD
-//
+
 #define SRVDATA_ARR_SIZE 14 // Размер блока памяти для вспомогательных данных
 #define Pnom_ARR_SIZE 8   // Макс. размер массива записанных номинальных мощностей ТЭНа sizeof(Pnom_arr)/sizeof(Pnom_arr[0])
 #define PDMset_ARR_SIZE 6     // Размер массива уставок мощности ТЭНа sizeof(PDMset)/sizeof(PDMset[0]) (ставить не меньше 4)
@@ -169,16 +161,5 @@
 #else
   #define ARRAY_SIZE PDMset_ARR_SIZE  // Размер массива для работы с номиналами в начальном меню и уставками
 #endif
-//
-#ifdef __AVR_ATmega168__  // Выключение UARTа, чтобы влезало в 168атмегу
-  #undef USE_USART
-  #undef USE_RMVK
-  #undef USE_ADprotocol
-#else
-  #ifdef USE_USART
-    static uint8_t cnt_uartWDT;     // Счетчик секунд для организации отсчета ожидания окончания посылки по USART
-  #endif
-#endif
-//
 
 #endif // __STAB_PARAM_H__

+ 334 - 0
lib/stab/usart.c

@@ -0,0 +1,334 @@
+#include "usart.h"
+
+#ifdef USE_USART
+/**
+ * @brief USART initialization
+ * Если задействовано управление регулятором ТЭНа через UART, инициализируем оный
+ */
+void USART_start(void) {
+  Serial.begin(9600, SERIAL_8N1); // Инициализируем USART
+#ifdef Debug
+  Serial.println("Started");
+#endif
+}
+
+/**
+ * @brief Функция возвращает значение текущей мощности с учетом режима "Разгон"
+ */
+static uint16_t get_Power(void) { 
+  if (fl.stab_off || fl.Udown || fl.NotZero) {  // Если авария или сеть в дауне - передаем ноль
+    return 0;
+  }
+  else if (fl.razg_on || fl.Ulow) { // В разгоне и при недостаточном сетевом передаем расчетную текущую мощность
+    uint32_t tmp_u = (long)U_sum * U_sum;
+    tmp_u /= 100;
+    tmp_u *= Pnom;
+    tmp_u /= U_LINE_Q;
+    return tmp_u;
+  } else { // В рабочем режиме - передаем уставку
+    return Pust;
+  }
+}
+
+/**
+ * @brief Функция установки текущей мощности по запросу
+ */
+static void set_newPDM(uint16_t power) {
+  if (power >= Pnom) { // Если параметр превышает максимально возможное значение,
+    PDMust = CICLE; // значит ставим максимально возможное значение.
+  } else {
+    PDMust = calc_proportion(power, CICLE, Pnom);
+  }
+
+  set_Pust(); // Пересчитаем Pust
+  fl.dspNewData = 1; // Обновление информации на дисплее
+}
+#endif /* USE_USART */
+
+#ifdef USE_ADprotocol
+/* USART */
+
+/**
+ * Байт "состав данных" b00010111 (основной параметр - мощность в нагрузке,
+ * доп. параметр - напряжение сети) в HEX-формате 0x17
+ */
+static char USART_InfoData[14] = {'T','1','7','0','0','0','0','0','0','0','0','0','0',0x0D};  // Массив готовых данных для передачи внешнему контроллеру
+static char USART_SetData[6];  // Массив управляющих символов от внешнего контроллера
+
+/**
+ * @brief Парсим управляющую последовательность по универсальному протоколу
+ */
+void USART_parser(void) {
+  static uint8_t index = 0;
+  static uint8_t data_size;
+
+  while (Serial.available() > 0) {
+    if (fl.stab_off) {
+      Serial.read(); // Вычитываем очередной байт, чтобы не засирать буфер
+    } else if ( !index || fl.uartTimeout ) {   // Начало
+      USART_SetData[0] = Serial.read(); // Вычитываем очередной байт
+      fl.uartTimeout = 0;               // Сбросим флаг таймаута ожидания окончания посылки
+      cnt_uartWDT = 0;                  // Сбросим таймер ожидания окончания посылки
+      switch ( USART_SetData[0] ) {     // Ждём первый символ...
+        case 'M':
+        case 'm': {                     // ...запроса на изменение режима работы
+          data_size = 2;
+          index=1;
+          break;
+        }
+        case 'P':
+        case 'p': {                     // ...запроса на изменение уставки
+          data_size = 5;
+          index=1;
+          break;
+        }
+        default: {
+          // break;
+        }
+      }
+    } else {
+      USART_SetData[index] = Serial.read(); // Вычитываем очередной байт
+      if ( USART_SetData[index] == 0x0D ) { // Ждем последнего символа посылки <CR>
+        if ( index == data_size ) {
+          switch (index) {
+            case 2: { // Парсим запрос на смену режима
+              switch ( USART_SetData[1] ) {
+                case '0': { // Переход в рабочий режим
+                  if ( fl.razg_on ) {
+                    stop_razgon();
+                  }
+                  break;
+                }
+                case '1': { // Переход в режим разгона
+                  if ((!fl.NotZero) & (!fl.Udown) & (!fl.razg_off)) {  // Если электросеть в дауне или разгон запрещен - не разгонишься
+                    fl.razg_on = 1;
+                    fl.razg = 1;
+                  }
+                  break;
+                }
+                case '2': { // Отключение нагрузки
+                  PDMust = 0;
+                  stop_razgon();
+                  Pust = 0;
+                  break;
+                }
+                default: {
+                  break;
+                }
+              }
+              break;
+            }
+            case 5: { // Парсим запрос на смену уставки
+              uint16_t tmp_p = 0;
+              uint8_t b;
+              for (uint8_t x=1; x <= 4; x++ ) {
+                tmp_p *= 16;
+                b = A_to_HEX (USART_SetData[x]);
+                if (b == 255) {
+                  break;
+                }
+                tmp_p += b;
+              }
+              if (b != 255) {
+                set_newPDM (tmp_p);   // Установим новую уставку мощности;
+              }
+              break;
+            }
+          }
+          index = 0;
+          fl.dspNewData = 1;  //Обновление информации на дисплее
+        } else index = 0;
+      } else if ( index++ == data_size ) {
+        index = 0;
+      }
+    }
+  }
+}
+
+/**
+ * @brief Отчет внешнему контроллеру по универсальному протоколу
+ */
+void USART_report(void) {
+  uint16_t b;
+
+  if (fl.stab_off) {
+    b = 3;  // b000000(11) - аварийное отключение нагрузки (удаленное включение невозможно)
+  } else if (fl.Udown || fl.NotZero) {
+    b = 6;  // b000001(10) - отсутствие сетевого напряжения, нагрузка отключена
+  } else if (fl.razg_on) {
+    b = 1;  // b(000000)(01) - разгон
+  } else if (PDMust == 0) {
+    b = 2;  // b000000(10) - нагрузка отключена
+  } else if (fl.Ulow) {
+    b = 8;   // b000010(00) - напряжения сети недостаточно для достижения уставки
+  } else {
+    b = 0;  // b000000(00) - режим рабочий, ошибок нет
+  }
+
+  // Закодируем состав данных
+  USART_InfoData[3] = HEX_to_A ( b / 16 );  // Старший разряд байта "Режим + ошибки"
+  USART_InfoData[4] = HEX_to_A ( b % 16 );  // Младший разряд байта "Режим + ошибки"
+
+  // Закодируем основной параметр - мощность на выходе
+  b = get_Power();  // Получим текущую мощщу
+  USART_InfoData[8] = HEX_to_A ( b % 16 );  // 0 разряд основного параметра
+  b /= 16;
+  USART_InfoData[7] = HEX_to_A ( b % 16 );  // 1 разряд основного параметра
+  b /= 16;
+  USART_InfoData[6] = HEX_to_A ( b % 16 );  // 2 разряд основного параметра
+  USART_InfoData[5] = HEX_to_A ( b / 16 );  // 3 разряд основного параметра
+
+  // Закодируем доп.параметр - напряжение сети
+  if (fl.NotZero) { // Если сети нет, то и на выходе пусто
+    USART_InfoData[12] = '0'; // 0 разряд основного параметра
+    USART_InfoData[11] = '0'; // 1 разряд основного параметра
+    USART_InfoData[10] = '0'; // 2 разряд основного параметра
+    USART_InfoData[9]  = '0'; // 3 разряд основного параметра
+  } else {
+    b = U_sum;
+    USART_InfoData[12] = HEX_to_A ( b % 16 ); // 0 разряд основного параметра
+    b /= 16;
+    USART_InfoData[11] = HEX_to_A ( b % 16 ); // 1 разряд основного параметра
+    b /= 16;
+    USART_InfoData[10] = HEX_to_A ( b % 16 ); // 2 разряд основного параметра
+    USART_InfoData[9]  = HEX_to_A ( b / 16 ); // 3 разряд основного параметра
+  }
+  // Отправим
+  Serial.write(USART_InfoData, 14);
+}
+#endif /* USE_ADprotocol */
+
+#ifdef USE_RMVK
+/* RMVK_/_Samovar */
+
+/**
+ * @brief Функция возвращает значение текущего напряжения без десятичного знака
+ */
+uint16_t get_Uin(void) {
+  return ((U_real_dec < 5)? U_real : (U_real + 1));
+}
+
+/**
+ * @brief Функция возвращает расчетное значение текущего (если getReal=true)
+ * или желаемого (если getReal=false) напряжения
+ */
+uint16_t get_Uout(const bool getReal) {
+  if ( fl.Udown || fl.NotZero || (PDMust == 0) ) {    // Если сеть в дауне или стаб в стопе - передаем ноль
+    return 0;
+  } else if ( getReal && ( fl.razg_on || fl.Ulow ) ) {  // В разгоне и при недостаточном сетевом передаем текущее сетевое, если надо
+    return get_Uin();
+  } else {    // В рабочем режиме - передаем уставку
+    return calc_proportion(PDMust, U_LINE);
+  }
+}
+
+/**
+ * @brief Парсим управляющую последовательность от RMVK_/_Samovar
+ */
+void USART_parser(void) {
+  static String inoutString;
+  static uint8_t index = 0;
+
+  while (Serial.available() > 0) {
+    char inChar = (char)Serial.read();// Вычитываем очередной байт
+    if ( !index || fl.uartTimeout ) { // Начало посылки
+      if ((inChar == 'A') || ((uint8_t)(inChar) == 0xD0)) { // Ждём первый символ посылки "A" или первый байт UTF-кириллицы из протокола Samovar'a
+        inoutString = inChar;
+        index=1;
+        fl.uartTimeout = 0; // Сбросим флаг таймаута ожидания окончания посылки
+        cnt_uartWDT = 0;    // Сбросим таймер ожидания окончания посылки
+      }
+    } else if ( index++ < 13 ) {  // Пока посылка не длиннее 13 символов, считаем её корректной
+      if ( inChar == 0x0D ) {   // Ждем последнего символа посылки <CR>
+        index = 0;
+        // Парсим строку, поскольку кончилась
+        // В протоколе Samovar стандартное начало посылки "АТ" пересылается русскими символами в Юникоде. Баг или фича?
+        if (( inoutString == ("AT+VI?")) ||  // Запрос текущего напряжения сети
+            ( inoutString == ("АТ+VI?"))) {  // В этой строке "АТ" - русскими символами!
+          if (fl.NotZero) { // Если сети нет, то и на выходе пусто
+            inoutString = String(0);
+          } else {
+            inoutString = String(get_Uin());
+          }
+        } else if (( inoutString == F("АТ+VO?")) || ( inoutString == F("АТ+VS?"))) {  // Запрос текущей мощности от Samovar. В этой строке "АТ" - русскими символами!
+          inoutString = String(get_Power());
+        } else if ( inoutString == F("AT+VO?") ) {  // Запрос текущего напряжения на выходе от РМВ-К
+          inoutString = String(get_Uout(true));
+        } else if ( inoutString == F("AT+VS?") ) {  // Запрос напряжения уставки на выходе от РМВ-К
+          inoutString = String(get_Uout(false));
+        } else if ( inoutString == F("AT+ON?") ) {  // Запрос состояния выхода от РМВ-К
+          if ((PDMust == 0) || (fl.NotZero) || (fl.Udown)) {  // Если на выходе 0
+            inoutString = String("OFF");
+          } else {
+            inoutString = String("ON");
+          }
+        } else if (( inoutString == F("AT+SS?")) ||   // Запрос режима от Samovar
+                 ( inoutString == F("АТ+SS?"))) {    // В этой строке "АТ" - русскими символами!
+          if (fl.stab_off || fl.Udown || fl.NotZero) {  // При аварии, сильно пониженном напряжении сети или его отсутствии - передаем ошибку
+            inoutString = String(3);
+          } else if (fl.razg_on) {            // Передаем "Разгон"
+            inoutString = String(1);
+          } else if (PDMust == 0) {           // Передаем "Стоп"
+            inoutString = String(2);
+          } else {                            // Передаем "Рабочий режим"
+            inoutString = String(0);
+          }
+        } else if (( inoutString == F("AT+ON=0")) ||  // Запрос на выключение стабилизатора
+                 ( inoutString == F("АТ+ON=0"))) {   // В этой строке "АТ" - русскими символами!
+          if (!fl.stab_off) {  // Если стаб не выключен аварийно...
+            PDMust = 0;
+            stop_razgon();
+            Pust = 0;
+            fl.dspNewData = 1;  //Обновление информации на дисплее
+            inoutString = "";
+          }
+        } else if (( inoutString == F("AT+ON=1")) ||  // Запрос на включение режима "Разгон"
+                 ( inoutString == F("АТ+ON=1"))) {   // В этой строке "АТ" - русскими символами!
+          if ((!fl.stab_off) && (!fl.NotZero) && (!fl.Udown) && (!fl.razg_off)) {  // Если авария, электросеть в дауне или разгон запрещен - не разгонишься
+            fl.razg_on = 1;
+            fl.razg = 1;
+            fl.dspNewData = 1;  //Обновление информации на дисплее
+          }
+          inoutString = "";
+        } else if ( inoutString.substring(0,8) == F("АТ+VS=") ) {  // Запрос на изменение уставки от Samovar. В этой строке "АТ" - русскими символами!
+          if (!fl.stab_off) {  // Если стаб не выключен аварийно...
+            //выключаем разгон, на всякий случай
+            stop_razgon();
+            set_newPDM (inoutString.substring(8).toInt());        // Установим новую уставку мощности
+            inoutString = "";
+          }
+        } else if ( inoutString.substring(0,6) == F("AT+VS=") ) { // Запрос на изменение уставки от РМВ-К
+          if (fl.stab_off || fl.Udown || fl.NotZero) {  // Если авария или сеть в дауне - ничего не меняем, передаем ошибку
+            inoutString = String(F("error"));
+          } else {
+            uint16_t tmp_u = inoutString.substring(6).toInt();
+            if ( tmp_u < U_LINE ) {
+              tmp_u *= CICLE;
+              PDMust = tmp_u / U_LINE;
+            } else PDMust = CICLE;
+            //выключаем разгон, на всякий случай
+            stop_razgon();
+            set_Pust();         // Посчитаем Pust
+            fl.dspNewData = 1;  // Обновление информации на дисплее
+            inoutString = String(get_Uout(false));
+          }
+        } else {  // Неизвестная или закосяченная команда
+#ifdef Debug
+          inoutString = String(F("(o_O unknown!)"));
+#else
+          inoutString = "";
+#endif
+        }
+        if ( inoutString != "" )  {       // Если строка не пустая
+          inoutString += char(0x0D);    // Добавляем в конец <CR>
+          Serial.print( inoutString );  // Шлём!
+        }
+      } else {  // Еще не конец
+        inoutString += inChar;  // Добавляем и это лыко в строку
+      }
+    } else {  // Посылка длинновата, а значит - некорректна, начинаем сначала
+      index = 0;
+    }
+  }
+}
+#endif /* RMVK_/_Samovar */

+ 33 - 0
lib/stab/usart.h

@@ -0,0 +1,33 @@
+#ifndef __USART_H__
+#define __USART_H__
+
+//=====Настройки коммуникации по последовательному порту============
+#define USE_USART           // Раскомментить для инициализации общения стаба с внешним контроллером
+  #ifdef USE_USART
+  //#define USE_RMVK        // Раскомментить для включения общения с внешним контроллером по протоколу Samovar и/или РМВ-К
+  #ifndef USE_RMVK
+    #define USE_ADprotocol  // По умолчанию используется универсальный протокол
+  #endif
+#endif
+
+#ifdef __AVR_ATmega168__  // Выключение UARTа, чтобы влезало в 168атмегу
+  #undef USE_USART
+  #undef USE_RMVK
+  #undef USE_ADprotocol
+#else
+  #ifdef USE_USART
+    static uint8_t cnt_uartWDT;     // Счетчик секунд для организации отсчета ожидания окончания посылки по USART
+  #endif
+#endif
+//==================================================================
+
+void USART_start(void);
+static uint16_t get_Power(void);
+static void set_newPDM(uint16_t power);
+void USART_parser(void);
+void USART_report(void);
+uint16_t get_Uin(void);
+uint16_t get_Uout(const bool getReal);
+void USART_parser(void);
+
+#endif // __USART_H__