Explorar el Código

Continue transformation path to STM32...

Vladimir N. Shilov hace 1 año
padre
commit
95e6e42e45
Se han modificado 3 ficheros con 117 adiciones y 129 borrados
  1. 2 2
      lib/lib.mk
  2. 109 118
      lib/stab/stab.c
  3. 6 9
      lib/stab/stab_param.h

+ 2 - 2
lib/lib.mk

@@ -7,8 +7,8 @@ USERSRC =  $(USERLIB)/st7735/st7735.c \
 	$(USERLIB)/st7735/liberation_mono_7x10.c \
 	$(USERLIB)/st7735/liberation_mono_10x16.c \
 	$(USERLIB)/st7735/segoe_ui_7x10.c \
-	$(USERLIB)/st7735/arial_8_ukr.c
-#	$(USERLIB)/stab/stab.c
+	$(USERLIB)/st7735/arial_8_ukr.c \
+	$(USERLIB)/stab/stab.c
 #	$(USERLIB)/eeprom/eeprom.c
           
 # Required include directories

+ 109 - 118
lib/stab/stab.c

@@ -6,10 +6,12 @@
 //#include "stab_usart.h"
 #include "st7735.h"
 
+/* Defines */
 /* Версия скетча и длина версии скетча в символах для правильного вывода на дисплей */
 #define VERSION "v0.100"
 #define VERSION_LEN 6
 
+/* Variables */
 static uint16_t Pnom; // Номинальная мощность ТЭНа (хранится в EEPROM и устанавливается из менюшки)
 //const uint8_t ARRAY_SIZE = max(Pnom_ARR_SIZE,PDMset_ARR_SIZE);
 static uint16_t PDMset[2][ARRAY_SIZE] = {};  // Массив уставок мощности ТЭНа с адресами
@@ -31,6 +33,8 @@ static char buf[24];                  // common string buffer for chsnprintf()
 
 static volatile uint8_t PID_ust = LINE_FREQ; // Данные для установки регистра сравнения таймера2
 
+static virtual_timer_t second_vt, hz50_vt;
+
 /* Организуем флаги и индикаторы в структуру */
 static volatile struct flags {  // Флаги
   unsigned  dspRefresh : 1;   // Флаг выхода из режима меню / полного обновления экрана
@@ -61,18 +65,6 @@ static volatile struct flags {  // Флаги
 #endif
 } fl = {};  /* Инициализируем структуру с нулевыми членами */
 
-//static uint8_t fl_A;  // Байт флажков A
-//static uint8_t fl_B;  // Байт флажков B
-//static uint8_t fl_C;  // Байт флажков C
-//#define flA_dspRefresh  B00000001
-//#define flA_dspTimeout  B00000010
-//#define flA_dspNewData  B00000100
-//#define flA_uartUnhold  B00001000
-//#define flA_uartReport  B00010000
-//#define flA_uartTimeout B00100000
-//#define flA_writable    B01000000
-//#define flA_butt        B10000000
-
 static uint8_t cnt_Pnom_count;  // Количество предустановок мощности
 static uint8_t cnt_Pnom_number; // Номер активной предустановки мощности
 static uint8_t cnt_PDMcount;    // Счетчик для перебора уставок мощности ТЭНа
@@ -80,9 +72,31 @@ static uint8_t cnt_PDMcount;    // Счетчик для перебора уст
 static uint8_t cnt_menuWDT;     // Счетчик секунд для организации отсчета ожидания выхода из меню
 static uint8_t cnt_dspMenu;     // Индикатор режима меню
 
+/* Functions */
 static uint8_t X_position(const uint8_t x, const uint16_t arg, const uint8_t pix); // Функция возвращает начальную позицию по Х для десятичного числа, в зависимости от количества знаков в нём.
 static uint8_t X_centred(const uint8_t len); // Функция возвращает начальную позицию по Х для текста длинной len знаков, для размещения оного по центру дисплея.
 static uint16_t calc_proportion(const uint16_t multiplier1, const uint16_t multiplier2, const uint32_t divider);
+static void Buttons_(void);
+static void second_new_cb(virtual_timer_t *vtp, void *p);
+static void hz50_cb(virtual_timer_t *vtp, void *p);
+
+/*
+ * Threads
+ */
+
+/* BTN serving thread. */
+static THD_WORKING_AREA(waBTNThread, 256);
+static __attribute__((noreturn)) THD_FUNCTION(BTNThread, arg) {
+  (void)arg;
+  chRegSetThreadName("BTN_process");
+
+  systime_t time = chVTGetSystemTimeX();
+  while (true) {
+    time += TIME_MS2I(BTN_SCAN_PERIOD);
+    Buttons_();
+    chThdSleepUntil(time);
+  }
+}
 
 /*
  * ПРОЦЕДУРЫ И ФУНКЦИИ
@@ -243,38 +257,6 @@ static void change_arr_cell(uint16_t arr[2][ARRAY_SIZE], const uint8_t index, co
 }
 #endif
 
-/**
- * @brief Инициализация АЦП
- */
-static void ADC_init(void) {
-  ADMUX = 0;
-  ADMUX |= ( 1 << REFS0); // Задаем ИОН равный напряжению питания
-  ADMUX |= 0;             // Выбираем пин A0 для преобразования
-  ADCSRA |= (1 << ADPS2 ) | (1 << ADPS1) | (1 << ADPS0); // предделитель на 128
-  ADCSRA |= (1 << ADIE);  // Разрешаем прерывания по завершении преобразования
-  ADCSRA |= (1 << ADEN);  // Включаем АЦП
-}
-
-/**
- * @brief Инициализация таймеров
- */
-static void Timers_init(void) {
-  //---Инициализация таймера 0 для тактирования АЦП -------------
-  TCCR0A = 0;
-  TCCR0B = 0;
-  TCCR0A |= (1 << WGM01); // Счетчик работает в режиме CTC (сброс по совпадению)
-  TCCR0B |= (1 << CS01) | (1 << CS00); // Предделитель на 64 (на счетчик - 250 кГц)
-  OCR0A = T_ADC; // Определяет период запуска АЦП
-  TIMSK0 |= (1 << OCIE0A); // Разрешаем прерывания по совпадению с OCR0A
-  // Инициализация таймера 2 для формирования импульса нуля Zero
-  TCCR2A = 0;
-  TCCR2B = 0;
-  TCCR2A |= (1 << WGM21); // Счетчик работает в режиме CTC (сброс по совпадению)
-  TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20); // Предделитель на 1024 (сч. - 15.625 кГц/64мкс)
-  OCR2A = LINE_FREQ; // Прерывание с удвоенной частотой сети
-  TIMSK2 |= (1 << OCIE2A); // Разрешаем прерывания по совпадению с OCR2A
-}
-
 /**
  * @brief Подпрограмма обработки режима разгона
  * Обеспечивает шунтирование контактов контактного реле
@@ -282,7 +264,6 @@ static void Timers_init(void) {
  * в момент включения/выключения режима "Разгон"
  */
 static void Razgon_(void) {
-#define RELAY_SHUNTING_TIME 50  // количество полупериодов, в течение которых шунтируются контакты реле
   static uint8_t cnt_P_relay=0; // Счетчик полупериодов шунтирования контактного реле
   if (fl.razg_on &&             // Если включен разгон..
       !fl.TRelay &&             // ..и НЕ включено контактное реле
@@ -304,7 +285,8 @@ static void Razgon_(void) {
 static void PDM_(void) {
   if (fl.razg) {
     pdm = CICLE; // В режиме разгона твердотельное всегда открыто
-  }
+  } /* ??? поставити також fl.Tout та вийти. авіщо при разгоні все інше? */
+
   static int8_t ps = 0; // Текущее значение постоянной составляющей
   int32_t lev = pdm + pdm_err; // Текущий уровень с учетом ошибки дискретизации, сделанной на предыдущем полупериоде.
   // Текущее значение постоянной составляющей
@@ -397,7 +379,7 @@ static void Buttons_(void) {
             break;
           }
           switch (butt) {
-            case 1: { //-----Кнопкой "P-" перебираем записанные значения или уменьшаем значение Pnom
+            case 1: { //Кнопкой "P-" перебираем записанные значения или уменьшаем значение Pnom
               if (bt.no_select) { //Если не выбираем, а вводим значение,...
                 if (butt_force_count > 20) { // Если очень долго держим...
                   if (Pnom > 100) {
@@ -425,7 +407,7 @@ static void Buttons_(void) {
               butt_force_count++;
               break; //Закончили
             }
-            case 2: { //-----Кнопкой "P+" увеличиваем значение Pnom
+            case 2: { //Кнопкой "P+" увеличиваем значение Pnom
               if (butt_force_count > 20) {
                 if ((Pnom += 100) > 9999) {
                   Pnom=9999; // Если очень долго держим, прибавляем по соточке
@@ -443,29 +425,29 @@ static void Buttons_(void) {
               butt_force_count++;
               break; //Закончили
             }
-            case 4: { //-----Кнопкой "Стоп" пишем значение в память и выходим из менюшки
-              bt.writePnom = 1; // Ставим флаг записи нового значения Pnom в EEPROM
+            case 4: { //Кнопкой "Стоп" пишем значение в память и выходим из менюшки
 #ifdef USE_EEPROM
+              bt.writePnom = 1; // Ставим флаг записи нового значения Pnom в EEPROM
               fl.writable = 1;  // Ставим флаг записи уставок в EEPROM
 #endif
             }
-            case 8: { //-----Кнопкой "Разгон" выходим из менюшки
+            case 8: { //Кнопкой "Разгон" выходим из менюшки
               if (Pnom < 10000) { // Если значение реальное...
                 cnt_Pnom_number = cnt_PDMcount; // Запомним порядковый номер выбранного Pnom
                 if (bt.no_select) {             // Если значение НЕ выбрано из записанных в EEPROM, а введено...
                   for (int8_t x = cnt_Pnom_count; x >= 0; x--) { // Проверим новое значение на совпадение с уже записанными
                     if (Pnom == PDMset[0][x]) { // Если такое значение уже есть в EEPROM...
                       cnt_Pnom_number = x;      // Запомним порядковый номер совпавшего Pnom
-                      bt.writePnom = 0;         // Снимем флаг записи нового значения Pnom в EEPROM
 #ifdef USE_EEPROM
+                      bt.writePnom = 0;         // Снимем флаг записи нового значения Pnom в EEPROM
                       fl.writable = 1;          // Ставим флаг записи уставок в EEPROM
 #endif
                       break;
                     }
                   }
                 } else {                        // Если значение выбрано из записанных в EEPROM...
-                  bt.writePnom = 0;             // Снимем флаг записи нового значения Pnom в EEPROM
 #ifdef USE_EEPROM
+                  bt.writePnom = 0;             // Снимем флаг записи нового значения Pnom в EEPROM
                   fl.writable = 1;              // Ставим флаг записи уставок в EEPROM
 #endif
                 }
@@ -476,12 +458,12 @@ static void Buttons_(void) {
                   EEPROM_read_PDMs();           // читаем ранее записанное
                 }
 #endif
-                if (bt.writePnom) { // Запишем новое значение Pnom, если необходимо
 #ifdef USE_EEPROM
+                if (bt.writePnom) { // Запишем новое значение Pnom, если необходимо
                   eeprom_update_word((uint16_t*)(cnt_Pnom_number * 2),Pnom);
-#endif
                   bt.writePnom = 0; // и сбросим флаг записи нового значения Pnom
                 }
+#endif
                 cnt_dspMenu = 0;    // Снимаем флаг перехода в меню
                 //
 #ifdef USE_USART
@@ -597,13 +579,13 @@ static void Buttons_(void) {
               }
               break;
             case 4:
-              if (PDMust == 0) {   Если мы не в меню и мощность ТЭНа нулевая, то...
+              if (PDMust == 0) { // Если мы не в меню и мощность ТЭНа нулевая, то...
                 cnt_dspMenu = 1;   //Ставим флаг перехода в меню
                 fl.dspRefresh = 1; //Ставим флаг обновления экрана
               } else {             //Если мы не в меню и мощность ТЭНа НЕнулевая, то...
                 remember_last_power_setting();// Запомним последнюю уставку
-                PDMust = 0;        // Экстренно выключим ТЭН
-                stop_razgon();     // Остановим разгон
+                PDMust = 0;        //Экстренно выключим ТЭН
+                stop_razgon();     //Остановим разгон
               }
               fl.butt = 0;         //После нажатия должна быть пауза
               break;
@@ -648,69 +630,20 @@ static void Buttons_(void) {
   }
 }
 
-/**
- * @brief Обработчик начала очередного полупериода по таймеру2
- */
-ISR(TIMER2_COMPA_vect) {
-  Razgon_();
-  if (pdm) {
-    PDM_();
-  } else {
-    fl.Tout = 0;   // Не будем зря теребить подпрограмму, если pdm = 0
-    //pdm_err = 0; // и обнулим ошибку дискретизации (а нужно ли?) TODO!
-  }
-
-  fl.PP_tm = !fl.PP_tm; // Инвертируем флаг полуволны
-  OCR2A = PID_ust; // Грузим новое значение в регистр сравнения
-
-  fl.Tout ? TURN_SSR_ON : TURN_SSR_OFF ;  // Включаем или выключаем ТЭН (твердотельное реле)
-  fl.TRelay ? TURN_RELAY_ON : TURN_RELAY_OFF ;  // Включаем или выключаем ТЭН (контактное реле)
-
-  sei(); // разрешим прерывания
-  // Считаем время
-  static uint8_t cnt_P_time=0;  // Счетчик полупериодов для организации отсчета времени
-  if (++cnt_P_time == P_TIME_MAX) {  // Уже секунду суммируем
-    cnt_P_time = 0;
-    //fl.dspNewData = 1;  // Раз в секунду не грех обновить дисплей, мало ли...
-    if ((cnt_dspMenu > 0) && (++cnt_menuWDT == MENU_TIMEOUT)) { // Если мы в меню и слишком долго не жмутся кнопки
-      fl.dspTimeout = 1;                                        // Установим флаг таймаута
-      cnt_menuWDT = 0;                                          // Сбросим таймер ожидания выхода из меню
-    }
-
-#ifdef USE_USART
-    if (++cnt_uartWDT == 10) {  // Если прошло уже 10 секунд от начала приема посылки по USART
-      fl.uartTimeout = 1;       // Установим флаг таймаута ожидания окончания посылки
-      cnt_uartWDT = 0;          // Сбросим таймер ожидания окончания посылки
-    }
-#endif
-#ifdef USE_ADprotocol
-    fl.uartReport = 1;          // пора слать рапорт
-#endif
-  }
-
-  Buttons_();   // Опрашиваем кнопки
-}
-
-/**
- * @brief Обработчик запуска преобразования АЦП по таймеру0
- */
-ISR(TIMER0_COMPA_vect) {
-  ADCSRA |=  (1 << ADSC); // Запуск преобразования
-}
 
 /**
  * @brief Обработчик окончания преобразования АЦП
  */
-ISR(ADC_vect) {
+static void ADC_cb(void) {
   static uint8_t TM2_current;
   static int16_t Ufir = 0;   // Буферная переменная для НЧ-фильтрации
   static int16_t Udelta = 0; // Буферная переменная для НЧ-фильтрации
   {
     int16_t U_adc;
     uint8_t TM2_tmp;
-    TM2_tmp = TCNT2;  // забрали значение из таймера синхронизации с сетью
-    U_adc = ADCL;
-    U_adc += ADCH << 8; // забрали результат преобразования АЦП
+    //!!! TM2_tmp = TCNT2;  // забрали значение из таймера синхронизации с сетью
+    //!U_adc = ADCL;
+    //!U_adc += ADCH << 8; // забрали результат преобразования АЦП
     U_adc -= U_ZERO;  // Убираем постоянную составляющую из оцифрованного сигнала
     { //Суммирование квадратов
       sum += (long)U_adc * U_adc; // Возводим в квадрат выборку АЦП и суммируем с предыдущими
@@ -761,13 +694,12 @@ ISR(ADC_vect) {
   }
 
   /* детекция перехода через ноль и ПИД-синхронизация */
-  sei(); // Следующие фрагменты длительны, но не требуют атомарности; разрешим прерывания
-
   if (fl.zero) { // ПИД-подстройка частоты внутреннего таймера к частоте сети
-    static uint16_t PID_reg = PID_ust << Km;   // Функция управления ПИД
-    static int32_t PID_err_old = 0;            // Разность фаз из предыдущего шага
-    static int32_t PID_int = 0;                // Интегральная составляющая из предыдущего шага
+    static uint16_t PID_reg = LINE_FREQ << Km; // Функция управления ПИД
+    static int32_t PID_err_old = 0; // Разность фаз из предыдущего шага
+    static int32_t PID_int = 0; // Интегральная составляющая из предыдущего шага
     int32_t temp_32 = (TM2_current + PHASE) << Km; // Разность фаз
+
     if (!fl.PP_tm) {
       temp_32 -= PID_reg + (1 << Km);  // Разность фаз должна быть с соответствующим знаком
     }
@@ -776,6 +708,7 @@ ISR(ADC_vect) {
     PID_reg += PID_int;
     PID_reg += ( temp_32 - PID_err_old ) >> Kd;
     PID_err_old = temp_32;
+
     // Готовим данные для записи в регистр сравнения таймера 2
     if ( PID_reg > (T_MAX << Km)) {
       PID_reg = (T_MAX << Km);  // Ограничим сверху
@@ -861,8 +794,14 @@ void Stab_Init(void) {
   TURN_SSR_OFF;   // Выключаем ТЭН (твердотельное реле)
   BTNS_ON;  // Activate buttons
 
-  ADC_init();     // Инициализируем АЦП
-  Timers_init();  // Инициализируем таймеры
+  // !!! TODO !!! Инициализируем АЦП
+
+  /* Starting a virtual timers. */
+  chVTSetContinuous(&hz50_vt, TIME_MS2I(LINE_PERIOD), hz50_cb, NULL);
+  chVTSetContinuous(&second_vt, TIME_MS2I(1000), second_new_cb, NULL);
+
+  /* Starting the Button thread. */
+  chThdCreateStatic(waBTNThread, sizeof(waBTNThread), NORMALPRIO, BTNThread, NULL);
 
   pp_Delay(20);   // Подождем 20 полупериодов
 
@@ -1138,3 +1077,55 @@ void Stab_WorkCycle(void) {
   }
 #endif
 }
+
+/**
+ * @brief Process asks for 1 second period
+ * 
+ * @param vtp 
+ * @param p 
+ */
+static void second_new_cb(virtual_timer_t *vtp, void *p) {
+  (void)vtp;
+  (void)p;
+
+  //fl.dspNewData = 1;  // Раз в секунду не грех обновить дисплей, мало ли...
+  if ((cnt_dspMenu > 0) && (++cnt_menuWDT == MENU_TIMEOUT)) { // Если мы в меню и слишком долго не жмутся кнопки
+    fl.dspTimeout = 1;                                        // Установим флаг таймаута
+    cnt_menuWDT = 0;                                          // Сбросим таймер ожидания выхода из меню
+  }
+
+#ifdef USE_USART
+  if (++cnt_uartWDT == 10) {  // Если прошло уже 10 секунд от начала приема посылки по USART
+    fl.uartTimeout = 1;       // Установим флаг таймаута ожидания окончания посылки
+    cnt_uartWDT = 0;          // Сбросим таймер ожидания окончания посылки
+  }
+#endif
+#ifdef USE_ADprotocol
+  fl.uartReport = 1;          // пора слать рапорт
+#endif
+}
+
+/**
+ * @brief Process tasks with line frewuency
+ * 
+ * @param vtp 
+ * @param p 
+ */
+static void hz50_cb(virtual_timer_t *vtp, void *p) {
+  (void)vtp;
+  (void)p;
+
+  Razgon_();
+  if (pdm) {
+    PDM_();
+  } else {
+    fl.Tout = 0;   // Не будем зря теребить подпрограмму, если pdm = 0
+    //pdm_err = 0; // и обнулим ошибку дискретизации (а нужно ли?) TODO!
+  }
+
+  fl.PP_tm = !fl.PP_tm; // Инвертируем флаг полуволны
+  // !!! OCR2A = PID_ust; // Грузим новое значение в регистр сравнения
+
+  fl.Tout ? TURN_SSR_ON : TURN_SSR_OFF ;  // Включаем или выключаем ТЭН (твердотельное реле)
+  fl.TRelay ? TURN_RELAY_ON : TURN_RELAY_OFF ;  // Включаем или выключаем ТЭН (контактное реле)
+}

+ 6 - 9
lib/stab/stab_param.h

@@ -19,22 +19,21 @@
 #define U_LINE 230        // Номинальное значение действующего напряжения в сети, для которого указана номинальная мощность ТЭНа
 #define U_MIN 180         // Значение напряжения в сети, ниже которого сеть считается аварийной
 #define U_LINE_Q 52900    // Квадрат номинала сети, для которого указана номинальная мощность ТЭНа
+#define RELAY_SHUNTING_TIME 50  // количество полупериодов, в течение которых шунтируются контакты реле
 //
 #define MENU_TIMEOUT 120  // Таймаут выхода из меню в секундах (не более 255)
-//
-
 //==================================================================
-//
 #ifdef U_LINE_FREQ_60
   #define LINE_FREQ 129 // Определяет начальную частоту для фазовой автоподстройки частоты сети (60,1Гц)
   #define PSUM_MAX 60   // Количество периодов для набора отсчетов АЦП (60 - это за 1 сек, это порядка 5000 отсчетов)
   #define P_TIME_MAX 120  // Количество полупериодов сети в секунду для отсчета времени
+  #define LINE_PERIOD 17
 #else
   #define LINE_FREQ 155   // Определяет начальную частоту для фазовой автоподстройки частоты сети (50,08Гц)
   #define PSUM_MAX 50     // Количество периодов для набора отсчетов АЦП (50 - это за 1 сек, это порядка 5000 отсчетов)
   #define P_TIME_MAX 100  // Количество полупериодов сети в секунду для отсчета времени
+  #define LINE_PERIOD 20
 #endif
-//
 //=============вход АЦП================
 #define pin_VACin 0       //  Пин входа измеряемого напряжения (A0)
 //
@@ -47,11 +46,6 @@
 
 //  Пин входа отключения стабилизатора (A2)
 #define pin_STAB_OFF_STATE palReadLine(LINE_STAB_OFF)
-//===========вЫходные выводы===========
-#define pin_PD_Out(pin) DDRD |=(1 << pin)   // Инициализация выхода
-#define pin_PD_HIGH(pin) PORTD |=(1 << pin) // Установка выхода
-#define pin_PD_LOW(pin) PORTD &=~(1 << pin) // Сброс выхода
-#define pin_PD_INV(pin) PORTD ^=(1 << pin)  // Инверсия выхода
 //===========выводы подключения кнопок===========
 #define pin_PB_STATE(pin) (~(PINB >> (pin - 8)) & 1) // Запрос состояния вывода со сдвигом и инверсией результата
 
@@ -110,4 +104,7 @@
   #define ARRAY_SIZE PDMset_ARR_SIZE  // Размер массива для работы с номиналами в начальном меню и уставками
 #endif
 
+// btn scan period, ms
+#define BTN_SCAN_PERIOD 20
+
 #endif // __STAB_PARAM_H__