Update CCS12 peripheral firmware and documentation
This commit is contained in:
@@ -1,3 +1,27 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Обработка измерительных каналов, индикации и защит устройства.
|
||||
*
|
||||
* @details
|
||||
* Модуль выполняет настройку каналов датчиков, фильтрацию результатов АЦП,
|
||||
* пересчёт токов, напряжений и температур в физические величины, формирование
|
||||
* диагностических признаков, а также подготовку масок передаваемых данных.
|
||||
* Обработчик CPU Timer1 обслуживает сторожевой таймер, период запуска CAN-обмена
|
||||
* и световую индикацию.
|
||||
*
|
||||
* Модуль использует глобальные параметры конфигурации, таблицы измерений и
|
||||
* аппаратные функции, объявленные в подключаемых заголовочных файлах проекта.
|
||||
*
|
||||
* @note Исходная исполняемая логика сохранена; добавлены комментарии
|
||||
* в формате Doxygen и поясняющие комментарии непосредственно внутри кода.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup sensor_processing Обработка датчиков и диагностик
|
||||
* @brief Инициализация, измерение, фильтрация и контроль измерительных каналов.
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
|
||||
#include "DSP2833x_SWPrioritizedIsrLevels.h"
|
||||
#include "filter_bat2.h"
|
||||
@@ -14,85 +38,187 @@
|
||||
#include "message.h"
|
||||
#include "log_to_mem.h"
|
||||
|
||||
#include <math.h> // Это чтобы мерить амплитуду! sqrt без этого будет крив!!!
|
||||
#include <math.h> // Это чтобы мерить амплитуду! sqrt без этого будет крив!!!
|
||||
|
||||
/**
|
||||
* @brief Состояние периодического запуска CAN-обмена.
|
||||
*
|
||||
* `CanPowse` увеличивается обработчиком таймера до значения `CANPOWSE`.
|
||||
* При достижении порога счётчик сбрасывается, а `CanGO` устанавливается в 1.
|
||||
*/
|
||||
unsigned int CanPowse=CANPOWSE,CanGO=0;
|
||||
|
||||
/**
|
||||
* @brief Битовые маски состава быстрых и медленных пакетов.
|
||||
*
|
||||
* Первый индекс выбирает режим передачи (`m_FAST` или `m_SLOW`), второй —
|
||||
* 16-битное слово общей таблицы данных.
|
||||
*/
|
||||
unsigned int Maska[2][8];
|
||||
|
||||
int READY=0; // Все загрузилос
|
||||
/** @brief Флаг завершения начальной загрузки и готовности модуля. */
|
||||
int READY=0; // Все загрузилос
|
||||
|
||||
int TPL_CANS=0; // Количество температурных каналов
|
||||
int tpl_cans=0; // Количество температурных датчиков
|
||||
int cal_addr=0; // Начало калибровочных датчиков
|
||||
int pow_addr=0; // Начало датчиков напражениа\тока
|
||||
/** @brief Настроенное количество температурных групп TPL для текущего режима. */
|
||||
int TPL_CANS=0; // Количество температурных каналов
|
||||
/** @brief Фактическое количество логических температурных каналов. */
|
||||
int tpl_cans=0; // Количество температурных датчиков
|
||||
/** @brief Индекс начала калибровочных каналов в общей таблице датчиков. */
|
||||
int cal_addr=0; // Начало калибровочных датчиков
|
||||
/** @brief Индекс начала каналов измерения напряжения и тока. */
|
||||
int pow_addr=0; // Начало датчиков напражениа\\тока
|
||||
|
||||
/**
|
||||
* @brief Периоды, используемые логикой готовности и мигания индикации.
|
||||
*/
|
||||
int period_ready, period_blink;
|
||||
|
||||
/**
|
||||
* @brief Накопленные и зафиксированные сводные признаки состояния.
|
||||
*
|
||||
* `chk` накапливает признаки в текущем цикле обработки каналов, а `sig`
|
||||
* получает снимок накопленного состояния при начале следующего цикла.
|
||||
*/
|
||||
FLAG chk,sig;
|
||||
/**
|
||||
* @brief Временные интервалы, выраженные в периодах обработки АЦП.
|
||||
*
|
||||
* Значения рассчитываются функцией Init_sensors() из частоты `ADC_FREQ`.
|
||||
*/
|
||||
unsigned int time_1_5sec, time_5msec, time_3sec, time_5sec;
|
||||
|
||||
/** @brief Счётчики выдержки пониженного уровня для двух силовых каналов. */
|
||||
unsigned int low_count[2] = {0,0};
|
||||
/** @brief Интегрирующие счётчики ошибок по фазным значениям. */
|
||||
unsigned int err_count[6];
|
||||
/** @brief Действующие значения измеряемых фаз, полученные из lev_quadr[]. */
|
||||
int lev_count[6];
|
||||
/** @brief Состояния фильтра квадратов мгновенных фазных значений. */
|
||||
float lev_quadr[6];
|
||||
/** @brief Оценки среднего, то есть нулевого, уровня четырёх каналов АЦП. */
|
||||
float zer_count[4];
|
||||
|
||||
/** @brief Тип каждого логического измерительного канала. */
|
||||
int sens_type[24];
|
||||
/** @brief Индекс парного канала для совместной диагностики и вычислений. */
|
||||
int sens_pair[24];
|
||||
/** @brief Счётчики фильтрации диагностик дискретных входов. */
|
||||
unsigned int din_count[32];
|
||||
|
||||
/** @brief Температуры, полученные от внешних измерительных устройств. */
|
||||
int ext_temp[8];
|
||||
/** @brief Диагностические состояния внешних температурных каналов. */
|
||||
int ext_diag[8];
|
||||
|
||||
/** @brief Рабочий счётчик времени процедуры калибровки. */
|
||||
unsigned int Caliber_time = 0;
|
||||
/** @brief Минимальный уровень тока, при котором контролируется перекос фаз. */
|
||||
int Curr_Edge;
|
||||
/**
|
||||
* @brief Маска состояний, исключающих канал из выбора опорных данных имитации.
|
||||
*/
|
||||
ERROR okay;
|
||||
|
||||
/**
|
||||
* @brief Коэффициенты пересчёта кодов АЦП в физические величины.
|
||||
*
|
||||
* Начальные значения соответствуют типам питающих каналов. Первые четыре
|
||||
* коэффициента могут быть переопределены функцией calc_sensor_koef() по
|
||||
* калибровочным значениям `Caliber[]`.
|
||||
*/
|
||||
float powK[] =
|
||||
{
|
||||
0.127, // питание 380В
|
||||
0.127, // питание 380В
|
||||
0.127, // питание 380В
|
||||
0.127, // питание 380В
|
||||
|
||||
1.000, // ток
|
||||
1.000, // напражение
|
||||
1.000, // ток
|
||||
1.000, // напражение
|
||||
|
||||
0.0076, // питание 31В
|
||||
0.0076, // питание 24В
|
||||
0.0076, // питание 24В
|
||||
0.0076, // питание 15В
|
||||
1.0000, // термодатчик мелкосхема
|
||||
0.0076, // питание 31В
|
||||
0.0076, // питание 24В
|
||||
0.0076, // питание 24В
|
||||
0.0076, // питание 15В
|
||||
1.0000, // термодатчик мелкосхема
|
||||
};
|
||||
|
||||
/** @brief Шаблон начального состояния цифрового фильтра. */
|
||||
FILTERBAT def_FILTERBAT = DEF_FILTERBAT;
|
||||
FILTERBAT adc_filter[20+4]; // макс.TPL*2 + 4 калибр
|
||||
FILTERBAT out_filter[20+4]; // макс.TPL*2 + 4 напраж
|
||||
FILTERBAT zer_filter[4]; // 4 напраж
|
||||
/** @brief Фильтры входных значений АЦП и калибровочных каналов. */
|
||||
FILTERBAT adc_filter[20+4]; // макс.TPL*2 + 4 калибр
|
||||
/** @brief Вторичные фильтры выходных и калибровочных значений. */
|
||||
FILTERBAT out_filter[20+4]; // макс.TPL*2 + 4 напраж
|
||||
/** @brief Фильтры оценок нулевого уровня четырёх силовых каналов. */
|
||||
FILTERBAT zer_filter[4]; // 4 напраж
|
||||
|
||||
/** @brief Рабочие счётчики состояний измерительных каналов. */
|
||||
long sens_count[24+4];
|
||||
|
||||
/** @brief Коэффициенты пересчёта температурных каналов групп 100 и 50. */
|
||||
float K100,K_50;
|
||||
/** @brief Коэффициенты пересчёта первой и второй температурных ветвей. */
|
||||
float K_T1,K_T2;
|
||||
|
||||
/**
|
||||
* @brief Обработчик прерывания CPU Timer1 для обслуживания датчиков и индикации.
|
||||
*
|
||||
* Обработчик обслуживает сторожевой таймер, формирует периодический флаг
|
||||
* `CanGO`, управляет индикацией готовности, аварии и нагрева, а в режиме
|
||||
* `dsk_LOAD` передаёт выполнение функции Load_runner().
|
||||
*
|
||||
* @note Функция использует статические счётчики ШИМ яркости и мигания ламп.
|
||||
*/
|
||||
interrupt void cpu_timer1_isr_SENS(void);
|
||||
|
||||
/********************************************************************/
|
||||
/* Расчет модула тока из показаний двух фаз */
|
||||
/********************************************************************/
|
||||
/**
|
||||
* @brief Рассчитывает модуль трёхфазного вектора по двум измеренным фазам.
|
||||
*
|
||||
* @param ia Мгновенное значение первой измеренной фазы.
|
||||
* @param ib Мгновенное значение второй измеренной фазы.
|
||||
*
|
||||
* @return Модуль результирующего вектора в тех же единицах, что и входы.
|
||||
*
|
||||
* @note Расчёт предполагает, что третья фаза может быть восстановлена из
|
||||
* условия `ia + ib + ic = 0`.
|
||||
*/
|
||||
float im_calc(float ia,float ib)
|
||||
{
|
||||
float isa,isb;
|
||||
|
||||
|
||||
/*
|
||||
* Из двух измеренных фаз формируются две взаимно перпендикулярные
|
||||
* составляющие результирующего трёхфазного вектора. Третья фаза
|
||||
* подразумевается равной -(ia + ib).
|
||||
*/
|
||||
isa = - 1.5 * (ia + ib);
|
||||
|
||||
/* COSPi6 = cos(pi/6) = sqrt(3)/2; эта составляющая зависит от разности фаз. */
|
||||
isb = COSPi6 * (ia - ib);
|
||||
|
||||
/* Нормировка 2/3 возвращает модуль в масштабе исходных фазных величин. */
|
||||
return (2*sqrt(isa*isa+isb*isb)/3);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Пересчитывает коэффициенты преобразования измерительных каналов.
|
||||
*
|
||||
* Для температурных каналов коэффициенты выбираются по активному варианту
|
||||
* `TermoRS` или `TermoAD`. Коэффициенты первых четырёх силовых каналов
|
||||
* рассчитываются из массива `Caliber[]` с учётом типа канала.
|
||||
*
|
||||
* @pre Разности калибровочных точек, используемые как делители, не равны нулю.
|
||||
* @post Обновлены `K100`, `K_50`, `K_T1`, `K_T2` и `powK[0..3]` в зависимости
|
||||
* от выбранной конфигурации.
|
||||
*/
|
||||
void calc_sensor_koef()
|
||||
{
|
||||
int i;
|
||||
float K;
|
||||
|
||||
/*
|
||||
* Вариант резистивных температурных датчиков. Коэффициент равен
|
||||
* заданному температурному диапазону, делённому на разность двух
|
||||
* сохранённых калибровочных кодов АЦП.
|
||||
*/
|
||||
if(TermoRS)
|
||||
{
|
||||
K100 = 129.0/(K150_D - K100_D);
|
||||
@@ -101,6 +227,10 @@ void calc_sensor_koef()
|
||||
K_T2 = 129.0/(K150_2 - K100_2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Вариант аналоговых термодатчиков: для каждой ветви используется
|
||||
* собственная пара калибровочных точек K300/K400.
|
||||
*/
|
||||
if(TermoAD)
|
||||
{
|
||||
K_T1 = 100.0/(K400_1 - K300_1);
|
||||
@@ -108,66 +238,101 @@ void calc_sensor_koef()
|
||||
}
|
||||
|
||||
// if(Kurrent)
|
||||
/* Перевод пользовательских калибровок первых четырёх каналов в float-масштаб. */
|
||||
for(i=0;i<4;i++)
|
||||
{
|
||||
K = Caliber[i];
|
||||
|
||||
/* Для тока и остальных величин калибровка хранится с разным десятичным масштабом. */
|
||||
if(sens_type[i]==CURRENT) K/=1000.0;
|
||||
else K/=10000.0;
|
||||
|
||||
powK[i] = K;
|
||||
} }
|
||||
|
||||
/** @copydoc cpu_timer1_isr_SENS */
|
||||
interrupt void cpu_timer1_isr_SENS(void)
|
||||
{
|
||||
/*
|
||||
* Статические переменные сохраняют фазу программного ШИМ яркости,
|
||||
* фазу мигания и требуемые логические состояния ламп между прерываниями.
|
||||
*/
|
||||
static unsigned int
|
||||
count_blink=0, count_bright=0, count_mode,
|
||||
blink_over, blink_alarm, work_lamp, heat_lamp, power_lamp;
|
||||
|
||||
/* Предыдущее состояние теста нужно для обнаружения фронта команды тестирования. */
|
||||
static int preTest;
|
||||
|
||||
/* Объединённый запрос теста ламп от двух источников управления. */
|
||||
int TST;
|
||||
|
||||
#define err_lamp work_lamp // это дла шкапа,
|
||||
#define alm_lamp heat_lamp // чтоб было понатнее
|
||||
#define err_lamp work_lamp // Для исполнения шкафа первый светодиод трактуется как «Ошибка».
|
||||
#define alm_lamp heat_lamp // Второй светодиод трактуется как «Авария».
|
||||
|
||||
/*
|
||||
* Разрешается запись в защищённые регистры, обновляется статистика таймера
|
||||
* и настраивается маска прерываний. EINT разрешает вложенные прерывания
|
||||
* с приоритетом, оставленным маской MINT13.
|
||||
*/
|
||||
EALLOW;
|
||||
CpuTimer1.InterruptCount++;
|
||||
IER |= M_INT13; // Set "global" priority
|
||||
IER &= MINT13; // Set "global" priority
|
||||
EINT;
|
||||
EDIS; // This is needed to disable write to EALLOW protected registers
|
||||
EDIS; // Запрет дальнейшей записи в защищённые EALLOW-регистры.
|
||||
|
||||
/* Во время активного сброса сторожевой таймер намеренно не обслуживается. */
|
||||
if(!cReset) ServiceDog();
|
||||
|
||||
/* Делитель частоты: один раз в CANPOWSE прерываний разрешается очередной CAN-цикл. */
|
||||
if(++CanPowse >= CANPOWSE)
|
||||
{
|
||||
CanPowse = 0;
|
||||
CanGO = 1;
|
||||
}
|
||||
|
||||
/* Тест включён, если установлен хотя бы один из двух командных флагов. */
|
||||
TST = cTestLamp|bTestLamp;
|
||||
|
||||
/*
|
||||
* Индикатор READY мигает при отсутствии ошибки; тест ламп принудительно
|
||||
* разрешает его переключение. Здесь применено побитовое OR для флагов 0/1.
|
||||
*/
|
||||
if(!sig.bit.Error|TST) toggle_READY();
|
||||
else clear_READY();
|
||||
|
||||
/* В исполнении нагрузки дальнейшей индикацией занимается отдельный автомат. */
|
||||
if(Desk==dsk_LOAD)
|
||||
{
|
||||
Load_runner();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Начало нового периода программного ШИМ яркости. Нужные выходы сначала
|
||||
* включаются согласно логическим состояниям ламп, а ниже будут выключены
|
||||
* при достижении значения Brightness.
|
||||
*/
|
||||
if(++count_bright == maximum_bright)
|
||||
{
|
||||
count_bright = 0 ;
|
||||
|
||||
/* Назначение дискретного RES_OUT_1 зависит от исполнения устройства. */
|
||||
if( ((Desk==dsk_SHKF) && power_lamp) ||
|
||||
((Desk==dsk_COMM) && heat_lamp) ) set_RES_OUT_1();
|
||||
else clear_RES_OUT_1();
|
||||
|
||||
if(work_lamp) set_LED_OUT_1(); // Она же err_lamp дла шкапа
|
||||
if(work_lamp) set_LED_OUT_1(); // Она же err_lamp для шкафа.
|
||||
else clear_LED_OUT_1();
|
||||
if(heat_lamp) set_LED_OUT_2(); // Она же alm_lamp дла шкапа
|
||||
if(heat_lamp) set_LED_OUT_2(); // Она же alm_lamp для шкафа.
|
||||
else clear_LED_OUT_2();
|
||||
}
|
||||
|
||||
/*
|
||||
* Конец активной части ШИМ. В режиме теста выходы не гасятся по Brightness,
|
||||
* поэтому лампы проверяются с полной заданной логической активностью.
|
||||
*/
|
||||
if(!TST)
|
||||
if(count_bright == Brightness)
|
||||
{
|
||||
@@ -176,6 +341,7 @@ interrupt void cpu_timer1_isr_SENS(void)
|
||||
clear_RES_OUT_1();
|
||||
}
|
||||
|
||||
/* На фронте теста немедленно запускается новый цикл мигания с известной фазы. */
|
||||
if(TST & !preTest)
|
||||
{
|
||||
count_blink = BLINK_TIME;
|
||||
@@ -183,13 +349,19 @@ interrupt void cpu_timer1_isr_SENS(void)
|
||||
}
|
||||
preTest = TST;
|
||||
|
||||
/* Обновление медленной фазы индикации выполняется один раз за BLINK_TIME. */
|
||||
if(++count_blink >= BLINK_TIME)
|
||||
{
|
||||
count_blink=0;
|
||||
count_mode++;
|
||||
|
||||
/* Быстрый шаблон меняет состояние на каждом шаге. */
|
||||
blink_over = (count_mode & 1)?1:0;
|
||||
|
||||
/* Медленный шаблон равен нулю только на каждом восьмом шаге. */
|
||||
blink_alarm = (count_mode & 7)?1:0;
|
||||
|
||||
/* При тесте все лампы получают один и тот же мигающий шаблон. */
|
||||
if(TST)
|
||||
{
|
||||
heat_lamp = blink_over;
|
||||
@@ -198,13 +370,18 @@ interrupt void cpu_timer1_isr_SENS(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Отдельная схема индикации для шкафа. */
|
||||
if(Mode==adr_SHKF)
|
||||
{
|
||||
/* Наличие питания показывается постоянно, пока нет общей ошибки. */
|
||||
power_lamp= 1;
|
||||
|
||||
/* Общая авария мигает на выходе alm_lamp. */
|
||||
if(sig.bit.Alarm){// power_lamp= blink_alarm;
|
||||
alm_lamp = blink_over; }
|
||||
else alm_lamp = 0;
|
||||
|
||||
/* Ошибка останова заставляет одновременно мигать питание и err_lamp. */
|
||||
if(sig.bit.Error){ power_lamp= blink_over;
|
||||
err_lamp = blink_over; }
|
||||
else err_lamp = 0;
|
||||
@@ -214,22 +391,45 @@ interrupt void cpu_timer1_isr_SENS(void)
|
||||
// if(sig.bit.Error) work_lamp = blink_over;
|
||||
// else if(sig.bit.Alarm) work_lamp = blink_alarm;
|
||||
// else
|
||||
/* В штатном режиме лампа работы горит непрерывно. */
|
||||
work_lamp = 1;
|
||||
|
||||
/* Секретная команда заменяет постоянное свечение медленным шаблоном. */
|
||||
if(bSecretBt|cSecretBt) work_lamp = blink_alarm;
|
||||
|
||||
/*
|
||||
* Температурная индикация имеет приоритет: перегрев — постоянно,
|
||||
* предупреждение — быстрое мигание, обрыв — короткий импульс
|
||||
* на каждом восьмом шаге.
|
||||
*/
|
||||
if(sig.bit.OverHeat) heat_lamp = 1;
|
||||
else if(sig.bit.SubHeat) heat_lamp = blink_over;
|
||||
else if(sig.bit.OutHeat) heat_lamp = !blink_alarm;
|
||||
else heat_lamp = 0;
|
||||
} } } }
|
||||
|
||||
/**
|
||||
* @brief Выполняет базовую инициализацию измерительных каналов.
|
||||
*
|
||||
* Функция определяет число и размещение каналов по `Mode`, назначает типы и
|
||||
* пары датчиков, сбрасывает фильтры и счётчики, настраивает специальные каналы
|
||||
* для выбранного исполнения `Desk`, а также переводит временные выдержки из
|
||||
* секунд в число периодов `ADC_FREQ`.
|
||||
*
|
||||
* @post Инициализированы таблицы `sens_type[]`, `sens_pair[]`, фильтры,
|
||||
* диагностические счётчики и маска `okay`.
|
||||
*/
|
||||
void Init_sensors()
|
||||
{
|
||||
int i;
|
||||
|
||||
|
||||
/* Сначала формируется пустая карта каналов; далее её заполняет выбранный Mode. */
|
||||
TPL_CANS = tpl_cans = cal_addr = 0;
|
||||
|
||||
/*
|
||||
* Определение числа температурных каналов и позиции калибровочных входов.
|
||||
* В режимах TRN/POW на одну группу TPL приходится два логических датчика.
|
||||
*/
|
||||
switch(Mode)
|
||||
{
|
||||
case adr_TRN1:
|
||||
@@ -246,17 +446,24 @@ void Init_sensors()
|
||||
case adr_SHKF: tpl_cans = TPL_SHK; cal_addr = tpl_cans-2; break;
|
||||
}
|
||||
|
||||
/* Силовые каналы размещаются после локальных и внешних температурных каналов. */
|
||||
pow_addr = tpl_cans + Owen;
|
||||
|
||||
/* Общие значения по умолчанию для всей статической таблицы датчиков. */
|
||||
for(i=0;i<24;i++)
|
||||
{
|
||||
/* Тип 0 означает, что специальная обработка каналу пока не назначена. */
|
||||
sens_type[i]=0;
|
||||
|
||||
/* До явной настройки парным считается сам канал. */
|
||||
sens_pair[i]=i;
|
||||
|
||||
/* Каждый канал получает независимое начальное состояние обоих фильтров. */
|
||||
adc_filter[i] = def_FILTERBAT;
|
||||
out_filter[i] = def_FILTERBAT;
|
||||
}
|
||||
|
||||
/* Назначение типа всем локальным температурным каналам. */
|
||||
if(TermoSW)
|
||||
for(i=0;i<tpl_cans;i++)
|
||||
{
|
||||
@@ -264,32 +471,45 @@ void Init_sensors()
|
||||
if(TermoAD) sens_type[i] = TERMO_AD;
|
||||
}
|
||||
|
||||
/* Внешние каналы OWEN следуют непосредственно за локальными температурами. */
|
||||
for(i=0;i<Owen;i++)
|
||||
{
|
||||
sens_type[tpl_cans+i] = TRM_OBEH;
|
||||
}
|
||||
|
||||
/* Настройка четырёхканального тракта измерения тока/напряжения. */
|
||||
if(Kurrent)
|
||||
{
|
||||
/* Ниже этого RMS-уровня контроль перекоса фаз не выполняется. */
|
||||
Curr_Edge = 200;
|
||||
|
||||
for(i=0;i<4;i++)
|
||||
{
|
||||
/* Базово все четыре силовых входа считаются каналами напряжения. */
|
||||
sens_type[pow_addr+i]=VOLTAGE;
|
||||
|
||||
/* XOR с 1 образует пары 0<->1 и 2<->3. */
|
||||
sens_pair[pow_addr+i]=pow_addr+(i^1);
|
||||
|
||||
/* Старт оценки нуля с последнего сохранённого значения. */
|
||||
zer_count[i] = Zero_lev[i];
|
||||
zer_filter[i] = def_FILTERBAT;
|
||||
} }
|
||||
|
||||
/* Для стенда нагрузки вторая пара силовых входов измеряет ток. */
|
||||
if(Desk==dsk_LOAD)
|
||||
{
|
||||
/* Для этого исполнения контроль перекоса начинается с более высокого уровня. */
|
||||
Curr_Edge = 300;
|
||||
|
||||
sens_type[2]=
|
||||
sens_type[3]=CURRENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* В шкафном исполнении карта каналов фиксирована аппаратной схемой:
|
||||
* первые шесть входов образуют три пары, остальные обрабатываются отдельно.
|
||||
*/
|
||||
if(Mode==adr_SHKF)
|
||||
{
|
||||
sens_type[0] = POWER_380; sens_pair[0]=1;
|
||||
@@ -310,45 +530,72 @@ void Init_sensors()
|
||||
sens_type[14] = TERMO_AD;
|
||||
}
|
||||
|
||||
/* Сброс принятой карты дискретных неисправностей. */
|
||||
DigErr.all=0;
|
||||
|
||||
/* Все выдержки ниже хранятся не в секундах, а в количестве циклов ADC_FREQ. */
|
||||
time_1_5sec = (3 * ADC_FREQ) / 2;
|
||||
time_5msec = (5 * ADC_FREQ) / 1000;
|
||||
time_3sec = (3 * ADC_FREQ);
|
||||
time_5sec = (5 * ADC_FREQ);
|
||||
|
||||
/*
|
||||
* Канал с любым из этих признаков не используется как исправный источник
|
||||
* при имитации температурного датчика в Temper_count().
|
||||
*/
|
||||
okay.all=0;
|
||||
okay.bit.Tear=1;
|
||||
okay.bit.Bypas=1;
|
||||
okay.bit.Ignor=1;
|
||||
okay.bit.Imit=1;
|
||||
|
||||
/* Сброс накопителей RMS и выдержек фазных неисправностей. */
|
||||
for(i=0;i<6; i++)
|
||||
{
|
||||
err_count[i] = 0;
|
||||
lev_count[i] = 0;
|
||||
lev_quadr[i] = 0;
|
||||
}
|
||||
|
||||
/* Сброс остальных счётчиков и буферов внешних температур. */
|
||||
for(i=0;i<28;i++) sens_count[i] = 0;
|
||||
for(i=0;i<32;i++) din_count[i] = 0;
|
||||
for(i=0;i<8; i++) ext_temp[i] = ext_diag[i] = 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Завершает инициализацию после загрузки рабочей конфигурации.
|
||||
*
|
||||
* Очищает оперативные измерения и признаки ошибок, восстанавливает нулевые
|
||||
* уровни, задаёт начальные значения для режима нагрузки, включает игнорирование
|
||||
* отдельных каналов в специальных исполнениях и пересчитывает коэффициенты.
|
||||
*
|
||||
* @pre Ранее вызвана Init_sensors(), а калибровочные параметры уже доступны.
|
||||
*/
|
||||
void Init_sensors_more()
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Удаляются динамические биты состояния, но сохраняются служебные биты,
|
||||
* разрешённые маской NOER. Параллельно очищаются измеренные значения.
|
||||
*/
|
||||
for(i=0;i<24;i++)
|
||||
{
|
||||
modbus[i] &= NOER;
|
||||
sens_data[i]=0;
|
||||
}
|
||||
|
||||
/* Восстановление сохранённых нулей силовых АЦП. */
|
||||
for(i=0;i<4;i++)
|
||||
{
|
||||
zer_count[i] = Zero_lev[i];
|
||||
|
||||
/*
|
||||
* Для LOAD до появления реального измерения задаются стартовые значения
|
||||
* напряжения, различающиеся для чётного и нечётного входа пары.
|
||||
*/
|
||||
if(Desk==dsk_LOAD)
|
||||
if(sens_type[i]==VOLTAGE)
|
||||
{
|
||||
@@ -356,6 +603,7 @@ void Init_sensors_more()
|
||||
else sens_data[i]=390;
|
||||
} }
|
||||
|
||||
/* В COMM все четыре силовых канала запускаются с установленным признаком Ignore. */
|
||||
if(Desk==dsk_COMM)
|
||||
{
|
||||
Modbus[pow_addr+0].bit.bitE = 1; // Ignore
|
||||
@@ -364,91 +612,186 @@ void Init_sensors_more()
|
||||
Modbus[pow_addr+3].bit.bitE = 1; // Ignore
|
||||
}
|
||||
|
||||
/* В LOAD игнорируются первые два канала общей таблицы. */
|
||||
if(Desk==dsk_LOAD)
|
||||
{
|
||||
Modbus[0].bit.bitE = 1; // Ignore
|
||||
Modbus[1].bit.bitE = 1; // Ignore
|
||||
}
|
||||
|
||||
/* Коэффициенты рассчитываются после того, как типы каналов окончательно назначены. */
|
||||
calc_sensor_koef();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Формирует маски данных быстрых и медленных пакетов обмена.
|
||||
*
|
||||
* Состав масок зависит от исполнения `Desk`, количества температурных каналов,
|
||||
* наличия измерения тока (`Kurrent`) и внешних каналов (`Owen`).
|
||||
*
|
||||
* @post Массив `Maska[m_FAST][]` и `Maska[m_SLOW][]` содержит актуальный набор
|
||||
* передаваемых диагностик, показаний, уставок и команд.
|
||||
*/
|
||||
void Init_packMask()
|
||||
{
|
||||
int i,j,can=0;
|
||||
|
||||
/* Количество каналов, для которых надо включить диагностику и показания. */
|
||||
can = pow_addr;
|
||||
|
||||
/* При наличии силового измерителя к базовой карте добавляются четыре канала. */
|
||||
if(Kurrent) can += 4;
|
||||
|
||||
/* Полная очистка двух наборов по восемь 16-битных слов. */
|
||||
for(i=0;i<2;i++)
|
||||
for(j=0;j<8;j++) { Maska[i][j]=0; }
|
||||
|
||||
switch(Desk)
|
||||
{
|
||||
case dsk_EPLT:
|
||||
Maska[m_SLOW][0]|= 0x000F; // Полученное
|
||||
Maska[m_SLOW][6]|= 0x01FC; // Яркость ламп и одиночество
|
||||
/* Для EPLT используется фиксированный сокращённый набор медленных данных. */
|
||||
Maska[m_SLOW][0]|= 0x000F; // Полученные данные.
|
||||
Maska[m_SLOW][6]|= 0x01FC; // Яркость ламп и служебные признаки.
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Для каждого канала включается один бит диагностики и один бит показания. */
|
||||
for(i=0;i<can;i++)
|
||||
{
|
||||
Maska[m_FAST][ i /16]|=(1<<( i %16)); // Диагностика
|
||||
Maska[m_FAST][(i+24)/16]|=(1<<((i+24)%16)); // Показаниа
|
||||
}
|
||||
for(i=0;i<3; i++)
|
||||
Maska[m_SLOW][i+3] = Maska[m_FAST][i]; // Уставки
|
||||
/* Диагностики занимают битовые позиции, начиная с 0. */
|
||||
Maska[m_FAST][ i /16]|=(1<<( i %16)); // Диагностика.
|
||||
|
||||
if(Desk==dsk_BKSD) Maska[m_SLOW][7]|= 0x0300; // Калибровка термодатчиков
|
||||
else
|
||||
Maska[m_SLOW][7]|= 0x0F00; // Калибровка термодатчиков
|
||||
if(Kurrent)
|
||||
{ Maska[m_SLOW][7]|= 0x000F; // Ноль датчиков напражениа
|
||||
Maska[m_SLOW][7]|= 0x00F0; // Калибровка датчиков напражениа
|
||||
Maska[m_FAST][6]|= 0x3F00; // Фазные значениа
|
||||
/* Показания размещены в том же пространстве со смещением 24 бита. */
|
||||
Maska[m_FAST][(i+24)/16]|=(1<<((i+24)%16)); // Показания.
|
||||
}
|
||||
if(Owen) Maska[m_FAST][6]|= 0x0008; // Период опроса OWEN
|
||||
|
||||
/* Первые три слова быстрой карты копируются в область масок уставок. */
|
||||
for(i=0;i<3; i++)
|
||||
Maska[m_SLOW][i+3] = Maska[m_FAST][i]; // Уставки.
|
||||
|
||||
/* BKSD передаёт две калибровки температуры, остальные исполнения — четыре. */
|
||||
if(Desk==dsk_BKSD) Maska[m_SLOW][7]|= 0x0300; // Калибровка термодатчиков.
|
||||
else
|
||||
Maska[m_SLOW][7]|= 0x0F00; // Калибровка термодатчиков.
|
||||
|
||||
if(Kurrent)
|
||||
{
|
||||
/* Четыре нулевых уровня и четыре коэффициента силовых каналов. */
|
||||
Maska[m_SLOW][7]|= 0x000F; // Ноль датчиков напряжения.
|
||||
Maska[m_SLOW][7]|= 0x00F0; // Калибровка датчиков напряжения/тока.
|
||||
|
||||
/* Шесть RMS-значений: по три фазы для двух измерительных групп. */
|
||||
Maska[m_FAST][6]|= 0x3F00; // Фазные значения.
|
||||
}
|
||||
/* При наличии OWEN включается поле периода его опроса. */
|
||||
if(Owen) Maska[m_FAST][6]|= 0x0008; // Период опроса OWEN.
|
||||
|
||||
if(Desk==dsk_LOAD)
|
||||
{ Maska[m_SLOW][7]|= 0x0030; // Калибровка ЦАП
|
||||
Maska[m_FAST][7]|= 0xE000; // Дублировать команды
|
||||
Maska[m_FAST][1]|= 0x3000; // Управлающий ток СИФУ
|
||||
{
|
||||
/* Дополнительные поля, используемые только стендом нагрузки. */
|
||||
Maska[m_SLOW][7]|= 0x0030; // Калибровка ЦАП.
|
||||
Maska[m_FAST][7]|= 0xE000; // Дублирование команд.
|
||||
Maska[m_FAST][1]|= 0x3000; // Управляющий ток СИФУ.
|
||||
} }
|
||||
|
||||
Maska[m_FAST][1]|= 0x0001; // Дискретные входы
|
||||
Maska[m_SLOW][6]|= 0x0007; // Яркость ламп, и периоды посылок
|
||||
Maska[m_SLOW][7]|= 0xE000; // Адрес, команды, и чтоб не вылазило
|
||||
/* Эти служебные поля включаются независимо от исполнения. */
|
||||
Maska[m_FAST][1]|= 0x0001; // Дискретные входы.
|
||||
Maska[m_SLOW][6]|= 0x0007; // Яркость ламп и периоды посылок.
|
||||
Maska[m_SLOW][7]|= 0xE000; // Адрес и командные поля.
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Фильтрует логическое условие интегрирующим счётчиком.
|
||||
*
|
||||
* При активном условии счётчик увеличивается до порога, после чего функция
|
||||
* возвращает 1. При снятии условия счётчик уменьшается; в переходной области
|
||||
* возвращается переданное предыдущее состояние `pre`.
|
||||
*
|
||||
* @param term Текущее логическое условие: ненулевое значение означает аварию.
|
||||
* @param count Указатель на накопительный счётчик условия.
|
||||
* @param edge Порог подтверждения условия в циклах вызова.
|
||||
* @param pre Состояние, сохраняемое на этапе накопления или отпускания.
|
||||
*
|
||||
* @return 1 при подтверждённом условии; 0 при полностью снятом условии;
|
||||
* иначе значение `pre`.
|
||||
*
|
||||
* @pre `count` указывает на доступную переменную счётчика.
|
||||
*/
|
||||
int er_anal(int term, unsigned int * count, long edge, int pre)
|
||||
{
|
||||
{
|
||||
/* Активное условие постепенно заполняет счётчик подтверждения. */
|
||||
if (term)
|
||||
{
|
||||
/* После достижения edge состояние считается устойчиво подтверждённым. */
|
||||
if((*count)>=edge) return 1;
|
||||
|
||||
/* До порога сохраняется предыдущее логическое состояние. */
|
||||
(*count)++; return pre;
|
||||
}
|
||||
}
|
||||
|
||||
/* При снятии условия подтверждение также отпускается постепенно. */
|
||||
if( (*count) == 0 ) return 0;
|
||||
(*count)--; return pre;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обновляет состояние ошибок выбранного датчика.
|
||||
*
|
||||
* Сохраняет служебные признаки, заданные маской `NOER`, добавляет вычисленные
|
||||
* ошибки, обновляет сводный признак останова `chk.bit.Error` и формирует признак
|
||||
* готовности канала как инверсию `Stop`.
|
||||
*
|
||||
* @param sens Индекс датчика в массиве `sens_error[]`.
|
||||
* @param err Новое вычисленное состояние ошибок канала.
|
||||
*/
|
||||
void reset_errs(int sens, ERROR err)
|
||||
{
|
||||
ERROR set;
|
||||
|
||||
/* Служебные флаги канала сохраняются, вычисляемые ошибки заменяются новыми. */
|
||||
set.all = (sens_error[sens].all & NOER) | err.all;
|
||||
|
||||
/* Общий Stop накапливается по всем каналам текущего цикла обработки. */
|
||||
chk.bit.Error|= set.bit.Stop;
|
||||
|
||||
/* Канал готов только тогда, когда его ошибки не требуют останова. */
|
||||
set.bit.Ready = !set.bit.Stop;
|
||||
|
||||
sens_error[sens] = set;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обрабатывает один канал измерения тока или напряжения.
|
||||
*
|
||||
* Функция отслеживает нулевой уровень АЦП, вычисляет мгновенное и действующее
|
||||
* значение фазы, а для второго канала пары — амплитуду результирующего вектора
|
||||
* и восстановленное действующее значение третьей фазы. Результаты записываются
|
||||
* в `sens_data[]` и диагностическую область `modbus[]`. Дополнительно выполняется
|
||||
* контроль перекоса фаз, верхнего и нижнего уровней.
|
||||
*
|
||||
* @param chan Относительный номер силового канала; допустимый диапазон задаётся
|
||||
* массивами четырёхканального измерительного тракта.
|
||||
*
|
||||
* @pre Выполнены Init_sensors() и Init_sensors_more(); индекс `chan` допустим
|
||||
* для `adc_table_lem[]`, `zer_count[]`, `powK[]` и связанных массивов.
|
||||
*/
|
||||
void Current_count(int chan)
|
||||
{
|
||||
/* Numb — рабочее значение; Zer — оценка нуля; Current — мгновенная фаза; Level — RMS. */
|
||||
float Numb,Zer,Current,Level;
|
||||
|
||||
/*
|
||||
* aCurrent хранит первую измеренную фазу до обработки второго канала пары.
|
||||
* Поэтому каналы каждой пары должны вызываться последовательно: чётный, затем нечётный.
|
||||
*/
|
||||
static float aCurrent,Amplitude;
|
||||
int sens, pair, ist, thrd, fazz, feed;
|
||||
|
||||
/* Индексы глобального датчика, пары, группы и восстановленной третьей фазы. */
|
||||
int ignor, sens, pair, ist, thrd, fazz, feed;
|
||||
|
||||
ERROR error;
|
||||
|
||||
/* В начале нового четырёхканального цикла фиксируется сводка прошлого цикла. */
|
||||
if(Desk == dsk_LOAD)
|
||||
if(!chan)
|
||||
{
|
||||
@@ -456,13 +799,25 @@ void Current_count(int chan)
|
||||
chk.all = 0;
|
||||
}
|
||||
|
||||
/* Переход от относительного номера силового входа к общей таблице датчиков. */
|
||||
sens = pow_addr + chan;
|
||||
|
||||
/* Парный канал нужен для сравнения фазных RMS-значений. */
|
||||
pair = sens_pair[chan];
|
||||
|
||||
/* Чётный канал — первая измеренная фаза пары, нечётный — вторая. */
|
||||
ist = !(chan & 1);
|
||||
|
||||
/* Номер двухканальной измерительной группы: 0 для каналов 0/1, 1 для 2/3. */
|
||||
feed = (chan >>1);
|
||||
|
||||
/* Начальная позиция трёх фаз этой группы в диагностической области Modbus. */
|
||||
fazz = feed*3;
|
||||
|
||||
/* Индекс накопителя RMS восстановленной третьей фазы: 4 либо 5. */
|
||||
thrd = feed+4;
|
||||
|
||||
/* Bypass полностью исключает канал из измерения, сохраняя сам флаг Bypass. */
|
||||
if(sens_error[sens].bit.Bypas)
|
||||
{
|
||||
sens_error[sens].all = 0;
|
||||
@@ -471,13 +826,23 @@ void Current_count(int chan)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Сырой код специализированного АЦП/датчика LEM. */
|
||||
Numb = adc_table_lem[chan];
|
||||
|
||||
/* Сохранение сырого кода в отдельной диагностической области. */
|
||||
modbus[0x64+chan] = Numb;
|
||||
|
||||
// Средний (он же нулевой) уровень показаний АЦП
|
||||
// Средний (он же нулевой) уровень показаний АЦП.
|
||||
/*
|
||||
* Первый IIR-слой медленно ведёт оценку к текущему коду. При вызове
|
||||
* ADC_FREQ раз в секунду коэффициент соответствует примерно 5-секундной базе.
|
||||
*/
|
||||
zer_count[chan] += (Numb-zer_count[chan])/(5.0 * ADC_FREQ);
|
||||
|
||||
/* Второй фильтр подавляет остаточную пульсацию оценки нулевого уровня. */
|
||||
Zer = (int)(filterbat(&zer_filter[chan],zer_count[chan]));
|
||||
|
||||
/* Сохранённый рабочий ноль обновляется только в разрешённой части WAKE-цикла. */
|
||||
if(WAKE < (WAKE_TIME /2)) Zero_lev[chan] = Zer;
|
||||
/*
|
||||
Test_mem_limit(12);
|
||||
@@ -485,40 +850,53 @@ Log_to_mem((int)adc_table_lem[chan]);
|
||||
Log_to_mem((int)zer_count[chan]);
|
||||
Log_to_mem((int)Zero_lev[chan]);
|
||||
*/
|
||||
// Мгновенное значение фазы
|
||||
// Мгновенное значение фазы.
|
||||
/* Удаление смещения АЦП и масштабирование в физическую величину. */
|
||||
Current = (Numb - Zero_lev[chan]) * powK[chan];
|
||||
|
||||
// Действующее (среднеквадратичное) значение фазы
|
||||
// Действующее (среднеквадратичное) значение фазы.
|
||||
/* Низкочастотная фильтрация квадрата мгновенного значения. */
|
||||
lev_quadr[chan] += ((Current*Current)-lev_quadr[chan])/(1.0 * ADC_FREQ);
|
||||
|
||||
/* Корень из среднего квадрата даёт RMS текущей измеренной фазы. */
|
||||
lev_count[chan] = sqrt(lev_quadr[chan]);
|
||||
|
||||
// Запомним
|
||||
// Первая фаза пары запоминается до прихода второй.
|
||||
if(ist)
|
||||
{
|
||||
/* В COMM полярность первой фазы аппаратно/логически развёрнута. */
|
||||
if(Desk==dsk_COMM) aCurrent =-Current;
|
||||
else aCurrent = Current; // Запомнили мгновенное значение - дла амплитуды
|
||||
else aCurrent = Current; // Мгновенное значение первой фазы для расчёта вектора.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Вычисление амплитуды
|
||||
// Вычисление амплитуды по двум измеренным фазам.
|
||||
/* im_calc() строит модуль трёхфазного вектора, затем результат фильтруется. */
|
||||
Amplitude = filterbat(&out_filter[sens], im_calc(Current,aCurrent));
|
||||
|
||||
/* Небольшие значения подавляются как шум; порог зависит от типа канала. */
|
||||
if(sens_type[sens]==VOLTAGE)
|
||||
if(Amplitude<20) Amplitude = 0;
|
||||
|
||||
if(sens_type[sens]==CURRENT)
|
||||
if(Amplitude<75) Amplitude = 0;
|
||||
|
||||
/* Для синусоиды действующее значение равно амплитуде, делённой на sqrt(2). */
|
||||
Level = Amplitude / RADIX2;
|
||||
|
||||
/* Во время WAKE вычисления ведутся, но рабочие выходные данные не обновляются. */
|
||||
if(!WAKE)
|
||||
{
|
||||
/* Нечётный элемент пары хранит амплитуду, предыдущий — RMS. */
|
||||
sens_data[sens ] = Amplitude;
|
||||
sens_data[sens-1] = Level;
|
||||
}
|
||||
|
||||
// Действующее (среднеквадратичное) значение третьей фазы
|
||||
// Действующее (среднеквадратичное) значение третьей фазы.
|
||||
/* Для симметричной трёхпроводной системы сумма мгновенных фаз равна нулю. */
|
||||
Numb =-Current-aCurrent;
|
||||
|
||||
/* Та же RMS-фильтрация применяется к восстановленной фазе. */
|
||||
lev_quadr[thrd] += (Numb*Numb-lev_quadr[thrd])/(1.0 * ADC_FREQ);
|
||||
lev_count[thrd] = sqrt(lev_quadr[thrd]);
|
||||
/*
|
||||
@@ -538,43 +916,60 @@ Log_to_mem(Amplitude);
|
||||
Log_to_mem(Level);
|
||||
}
|
||||
*/
|
||||
/* Публикация трёх RMS-фаз группы в последовательных Modbus-регистрах. */
|
||||
modbus[0x68+fazz ] = lev_count[pair];
|
||||
modbus[0x68+fazz+1] = lev_count[chan];
|
||||
modbus[0x68+fazz+2] = lev_count[thrd];
|
||||
}
|
||||
|
||||
// Это максимальное
|
||||
// Выбор максимального RMS из трёх фаз — опорного уровня для оценки перекоса.
|
||||
Numb = lev_count[chan];
|
||||
if(Numb<lev_count[pair]) Numb = lev_count[pair];
|
||||
if(Numb<lev_count[thrd]) Numb = lev_count[thrd];
|
||||
|
||||
// Зашиты!
|
||||
// Формирование защит.
|
||||
error.all = 0;
|
||||
if(WAKE || sens_error[chan].bit.Ignor) goto fin;
|
||||
|
||||
/* В переходном состоянии WAKE новые защиты не формируются. */
|
||||
if(WAKE) goto fin;
|
||||
|
||||
/* Ignore оставляет диагностику видимой, но ниже может запретить Stop. */
|
||||
ignor = sens_error[sens].bit.Ignor;
|
||||
|
||||
/*
|
||||
* Если измеренная фаза отстаёт от максимальной более чем на 30 % и общий
|
||||
* уровень выше Curr_Edge, условие должно сохраняться time_3sec циклов.
|
||||
*/
|
||||
if(er_anal( ((Numb-lev_count[chan])/Numb > 0.3) && (Numb>Curr_Edge),
|
||||
&err_count[chan],time_3sec,0))
|
||||
{
|
||||
error.bit.Wry = 1;
|
||||
error.bit.Stop = 1;
|
||||
if(!ignor) error.bit.Stop = 1;
|
||||
}
|
||||
|
||||
/* Аналогичный контроль для вычисленной третьей фазы. */
|
||||
if(er_anal( ((Numb-lev_count[thrd])/Numb > 0.3) && (Numb>Curr_Edge),
|
||||
&err_count[thrd],time_3sec,0))
|
||||
{
|
||||
error.bit.Wry = 1;
|
||||
error.bit.Stop = 1;
|
||||
if(!ignor) error.bit.Stop = 1;
|
||||
}
|
||||
|
||||
/* Полное игнорирование канала прекращает дальнейший контроль уровней. */
|
||||
if(ignor) goto fin;
|
||||
|
||||
/* Общий уровень пары проверяется один раз — при обработке её второго канала. */
|
||||
if(Desk == dsk_LOAD)
|
||||
if(!ist)
|
||||
{
|
||||
/* Верхняя уставка всегда считается аварийной и формирует Stop. */
|
||||
if(Level > sens_hi_edge[sens])
|
||||
{
|
||||
error.bit.Hyper = 1;
|
||||
error.bit.Stop = 1;
|
||||
}
|
||||
|
||||
/* Для напряжения пониженный уровень подтверждается трёхсекундной выдержкой. */
|
||||
if(sens_type[sens]==VOLTAGE)
|
||||
|
||||
if(er_anal( (Level < sens_lo_edge[sens]),
|
||||
@@ -584,6 +979,7 @@ modbus[0x68+fazz+2] = lev_count[thrd];
|
||||
error.bit.Stop = 1;
|
||||
}
|
||||
|
||||
/* Для тока та же sens_lo_edge используется как порог предупреждения Over. */
|
||||
if(sens_type[sens]==CURRENT)
|
||||
if(Level > sens_lo_edge[sens])
|
||||
{
|
||||
@@ -610,24 +1006,49 @@ Log_to_mem(WAKE);
|
||||
}
|
||||
*/
|
||||
fin:
|
||||
/* Объединение вычисленных ошибок со служебными флагами выбранного канала. */
|
||||
reset_errs(sens,error);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обрабатывает один температурный канал.
|
||||
*
|
||||
* Поддерживаются локальные значения АЦП, внешние температуры типа `TRM_OBEH`,
|
||||
* режим выдачи сырого кода, имитация по исправным однотипным датчикам и режим
|
||||
* калибровки. После пересчёта температура ограничивается диапазоном от -20 до
|
||||
* 200 градусов, записывается в `sens_data[]` с масштабом 0,1 градуса и
|
||||
* проверяется на обрыв, перегрев и предупредительный уровень.
|
||||
*
|
||||
* @param chan Индекс температурного канала в общих массивах датчиков.
|
||||
* @param own Ненулевое значение разрешает получение данных внешнего канала
|
||||
* `TRM_OBEH` из массивов `ext_temp[]` и `ext_diag[]`.
|
||||
*
|
||||
* @pre Индекс `chan` допустим для выбранной конфигурации каналов.
|
||||
*/
|
||||
void Temper_count(int chan, int own)
|
||||
{
|
||||
/* Рабочее значение: сначала код/внешние данные, затем температура в градусах. */
|
||||
float Numb;
|
||||
|
||||
/* Целая температура используется при сравнении с уставками диагностики. */
|
||||
static int Temper;
|
||||
|
||||
/* j — число доноров имитации; kun — индекс калибровочного канала. */
|
||||
int i,j, kun;
|
||||
long s;
|
||||
ERROR error;
|
||||
|
||||
/* Индекс внешнего температурного канала относительно начала блока OWEN. */
|
||||
int ovn;
|
||||
|
||||
/* Первый температурный канал открывает новый цикл накопления общих флагов. */
|
||||
if(!chan)
|
||||
{
|
||||
sig.all = chk.all;
|
||||
chk.all = 0;
|
||||
}
|
||||
|
||||
/* Bypass применяется здесь только к локальным температурным каналам. */
|
||||
if(sens_error[chan].bit.Bypas)
|
||||
if(chan<tpl_cans)
|
||||
{
|
||||
@@ -637,14 +1058,20 @@ void Temper_count(int chan, int own)
|
||||
return;
|
||||
}
|
||||
|
||||
/* По умолчанию источником служит локальная таблица результатов АЦП. */
|
||||
Numb = ADC_table[chan];
|
||||
|
||||
/* Сервисный режим публикует сырой код без пересчёта и диагностики. */
|
||||
if(cRawMeat)
|
||||
{
|
||||
sens_data[chan] = Numb;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Имитация заменяет локальный датчик средним кодом исправных датчиков
|
||||
* того же типа. Каналы с флагами из okay исключаются из набора доноров.
|
||||
*/
|
||||
if(sens_error[chan].bit.Imit)
|
||||
if(chan<tpl_cans)
|
||||
{
|
||||
@@ -659,27 +1086,37 @@ void Temper_count(int chan, int own)
|
||||
j++;
|
||||
} }
|
||||
|
||||
/* При наличии доноров вычисляется среднее; без доноров s остаётся равным нулю. */
|
||||
if(j) s/=j;
|
||||
Numb = s;
|
||||
}
|
||||
|
||||
/* Неотрицательный kun означает, что текущий канал расположен в зоне калибровки. */
|
||||
kun = chan - cal_addr;
|
||||
|
||||
/* При own!=0 внешний TRM_OBEH подменяет локальное значение АЦП. */
|
||||
if(own)
|
||||
if(sens_type[chan]==TRM_OBEH)
|
||||
{
|
||||
ovn = chan-tpl_cans;
|
||||
|
||||
/* Внешняя температура приходит в десятых долях градуса. */
|
||||
Numb= 0.1 * ext_temp[ovn];
|
||||
|
||||
/* Любая внешняя диагностика преобразуется в заведомо аварийное значение. */
|
||||
if(ext_diag[ovn])
|
||||
Numb = -100;
|
||||
}
|
||||
|
||||
/* Локальный аналоговый вариант температурного тракта. */
|
||||
if(!own)
|
||||
if(TermoAD)
|
||||
{
|
||||
/* В режиме калибровки сырой код записывается в соответствующую точку. */
|
||||
if(cTermoCal|bTermoCal)
|
||||
if(!WAKE)
|
||||
{
|
||||
/* В шкафном исполнении уровень кода выбирает старшую пару калибровок. */
|
||||
if(Desk==dsk_SHKF)
|
||||
if(Numb>2200) kun|=2;
|
||||
|
||||
@@ -690,21 +1127,25 @@ void Temper_count(int chan, int own)
|
||||
return;
|
||||
} }
|
||||
|
||||
/* Калибровочные/служебные позиции за пределами tpl_cans здесь не измеряются. */
|
||||
if(chan>=tpl_cans) return;
|
||||
|
||||
// Kun именно чтоб скомпенсировать нечетность
|
||||
// Нечётность kun выбирает одну из двух независимых аналоговых ветвей.
|
||||
if(kun&1) Numb = (Numb-K300_2)*K_T2+ZERO;
|
||||
else Numb = (Numb-K300_1)*K_T1+ZERO;
|
||||
}
|
||||
|
||||
/* Резистивный вариант температурного тракта. */
|
||||
if(TermoRS)
|
||||
{
|
||||
/* Каналы начиная с cal_addr используются как калибровочные, а не измерительные. */
|
||||
if(kun>=0)
|
||||
{
|
||||
/* Во время WAKE вторичный фильтр синхронизируется с первичным. */
|
||||
if(WAKE) out_filter[chan] = adc_filter[chan];
|
||||
else
|
||||
{
|
||||
// Калибровку отфильтруем вторым слоем
|
||||
// Калибровочный код дополнительно фильтруется вторым слоем.
|
||||
Numb = filterbat(&out_filter[chan],Numb);
|
||||
|
||||
TCaliber[kun] = Numb;
|
||||
@@ -715,29 +1156,39 @@ void Temper_count(int chan, int own)
|
||||
|
||||
if(Desk==dsk_BKSD)
|
||||
{
|
||||
/* Для диагностики BKSD сырой код дополнительно публикуется со смещением +8. */
|
||||
sens_data[chan+8] = ADC_table[chan];
|
||||
|
||||
/* Первые шесть и последующие каналы используют разные масштабы. */
|
||||
if(chan<6) Numb = (Numb-K100_D)*K100;
|
||||
else Numb = (Numb-K100_D)*K_50;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* В остальных исполнениях чётные и нечётные каналы имеют свои калибровки. */
|
||||
if(chan&1) Numb = (Numb-K100_2)*K_T2;
|
||||
else Numb = (Numb-K100_1)*K_T1;
|
||||
} }
|
||||
|
||||
/* Ограничение диапазона одновременно защищает формат данных и задаёт аварийные границы. */
|
||||
if(Numb < -20) Numb = -20;
|
||||
if(Numb > 200) Numb = 200;
|
||||
|
||||
/* Сервисная команда подменяет результат фиксированной температурой 40 °C. */
|
||||
if(bSecretBt|cSecretBt) Numb = 40.0;
|
||||
|
||||
/* В таблице температура хранится в десятых долях градуса. */
|
||||
sens_data[chan] = Numb*10;
|
||||
|
||||
/* Для пороговых сравнений дробная часть отбрасывается. */
|
||||
Temper = Numb;
|
||||
|
||||
error.all = 0;
|
||||
|
||||
/* Во время запуска или при Ignore показание сохраняется, но диагностика не обновляется. */
|
||||
if(WAKE || sens_error[chan].bit.Ignor) goto fin;
|
||||
|
||||
// Обрыв или неисправность
|
||||
// Обрыв или неисправность: результат упёрся в одну из границ допустимого диапазона.
|
||||
if(Temper<=-20 || Temper>=200)
|
||||
{
|
||||
error.bit.Tear = 1;
|
||||
@@ -745,7 +1196,11 @@ sens_data[chan+8] = ADC_table[chan];
|
||||
}
|
||||
else
|
||||
|
||||
// Перегрев
|
||||
// Перегрев.
|
||||
/*
|
||||
* Если Hyper уже был активен, он удерживается до снижения температуры
|
||||
* ниже верхней уставки на величину Cooling — это температурный гистерезис.
|
||||
*/
|
||||
if(((Temper>sens_hi_edge[chan]-Cooling) && (sens_error[chan].bit.Hyper)) ||
|
||||
(Temper>sens_hi_edge[chan]) )
|
||||
{
|
||||
@@ -755,7 +1210,7 @@ sens_data[chan+8] = ADC_table[chan];
|
||||
}
|
||||
else
|
||||
|
||||
// Предупреждение по температуре
|
||||
// Предупреждение по температуре проверяется только при отсутствии обрыва и перегрева.
|
||||
if(Temper>sens_lo_edge[chan])
|
||||
{
|
||||
error.bit.Over = 1;
|
||||
@@ -763,21 +1218,39 @@ sens_data[chan+8] = ADC_table[chan];
|
||||
}
|
||||
|
||||
fin:
|
||||
/* Запись локальной диагностики и обновление общего признака Stop. */
|
||||
reset_errs(chan,error);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обрабатывает канал контроля питающего напряжения.
|
||||
*
|
||||
* Код АЦП масштабируется коэффициентом, выбранным по типу датчика. Функция
|
||||
* фильтрует диагностические дискретные входы, контролирует пониженное напряжение
|
||||
* и формирует общий признак аварии. Останов по низкому напряжению формируется,
|
||||
* когда аналогичный признак присутствует и у парного канала.
|
||||
*
|
||||
* @param chan Индекс канала питания в таблицах `ADC_table[]`, `sens_type[]`,
|
||||
* `sens_pair[]` и `sens_error[]`.
|
||||
*
|
||||
* @note Проверка повышенного напряжения в исходном коде отключена блочным
|
||||
* комментарием и настоящей документацией не активируется.
|
||||
*/
|
||||
void Power_count(int chan)
|
||||
{
|
||||
/* Numb — сырой код АЦП; Power — пересчитанное напряжение; bitt — позиция диагностики. */
|
||||
float Numb;
|
||||
int Power,bitt;
|
||||
ERROR error;
|
||||
|
||||
/* Нулевой канал начинает новый цикл контроля питающих каналов. */
|
||||
if(!chan)
|
||||
{
|
||||
sig.all = chk.all;
|
||||
chk.all = 0;
|
||||
}
|
||||
|
||||
/* Bypass исключает канал, обнуляя его показание и оставляя флаг Bypass. */
|
||||
if(sens_error[chan].bit.Bypas)
|
||||
{
|
||||
sens_error[chan].all = 0;
|
||||
@@ -786,56 +1259,77 @@ void Power_count(int chan)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Получение последнего кода общего АЦП. */
|
||||
Numb = ADC_table[chan];
|
||||
|
||||
/* В сервисном режиме наружу передаётся сырой код без проверок. */
|
||||
if(cRawMeat)
|
||||
{
|
||||
sens_data[chan] = Numb;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Тип канала используется как индекс таблицы коэффициентов powK[]. */
|
||||
Power = Numb * powK[sens_type[chan]];
|
||||
|
||||
/* Рабочая таблица хранит уже пересчитанное физическое значение. */
|
||||
sens_data[chan] = Power;
|
||||
|
||||
error.all = 0;
|
||||
|
||||
/* При запуске и Ignore аналоговое значение обновляется, а защиты пропускаются. */
|
||||
if(WAKE || sens_error[chan].bit.Ignor) goto fin;
|
||||
|
||||
// Дискретные каналы
|
||||
// Дискретные каналы.
|
||||
/* Обычно каждому аналоговому каналу соответствуют два последовательных бита DigErr. */
|
||||
bitt = chan*2;
|
||||
if(chan!=7)
|
||||
{
|
||||
/* Каждый вход подтверждается интегрирующей выдержкой 1000 вызовов. */
|
||||
error.bit.Discr1 = er_anal(((DigErr.all>>bitt)&1), &din_count[bitt], 1000, 0); bitt++;
|
||||
error.bit.Discr2 = er_anal(((DigErr.all>>bitt)&1), &din_count[bitt], 1000, 0); bitt++;
|
||||
}
|
||||
|
||||
/* У шестого канала предусмотрены ещё два дополнительных дискретных признака. */
|
||||
if(chan==6)
|
||||
{
|
||||
error.bit.Discr3 = er_anal(((DigErr.all>>bitt)&1), &din_count[bitt], 1000, 0); bitt++;
|
||||
error.bit.Discr4 = er_anal(((DigErr.all>>bitt)&1), &din_count[bitt], 1000, 0); bitt++;
|
||||
}
|
||||
|
||||
/* Contr — сводный признак наличия хотя бы одной дискретной неисправности. */
|
||||
error.bit.Contr = error.bit.Discr1 | error.bit.Discr2 | error.bit.Discr3 | error.bit.Discr4;
|
||||
|
||||
// Пониженное напражение
|
||||
// Пониженное напряжение.
|
||||
if(Power <sens_lo_edge[chan])
|
||||
{
|
||||
error.bit.Out = 1;
|
||||
|
||||
/* Останов формируется только при одновременном снижении в парном канале. */
|
||||
if(sens_error[sens_pair[chan]].bit.Out)
|
||||
{
|
||||
error.bit.Stop = 1;
|
||||
} }
|
||||
|
||||
// Повышенное напражение
|
||||
// Повышенное напряжение.
|
||||
/*
|
||||
* Эта защита присутствует в исходнике, но намеренно отключена блочным
|
||||
* комментарием; исполняемый код её не учитывает.
|
||||
*/
|
||||
/* if(Power > sens_hi_edge[chan])
|
||||
{
|
||||
error.bit.Hyper = 1;
|
||||
error.bit.Stop = 1;
|
||||
}
|
||||
*/
|
||||
/* Любая локальная неисправность питания поднимает общую ламповую аварию. */
|
||||
if(error.all)
|
||||
chk.bit.Alarm = 1;
|
||||
|
||||
fin:
|
||||
/* Сохранение вычисленных ошибок и обновление общего Stop. */
|
||||
reset_errs(chan,error);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user