Перейти на компактные 12-словные записи v4 и сохранять 10 процентов банка после отключения. Добавить трёхсекундное ожидание роста ЗПТ и настраиваемый лимит пусков. Явно инициализировать состояние UART и гарантировать публикацию параметров EEPROM перед запуском Timer2. Сохранить воспроизводимые сборки, регрессионные проверки, документацию и обновлённые результаты сборки; исключить временные артефакты.
600 lines
55 KiB
C
600 lines
55 KiB
C
/* Выход ЦАП и автомат защиты нагрузки с проверкой напряжения и свежести CAN. Русские пояснения сохранены в UTF-8. */
|
||
/* Подключает "DSP2833x_Device.h": поставляемое TI описание Device для семейства C2833x. */
|
||
#include "DSP2833x_Device.h" // DSP281x Headerfile Include File
|
||
/* Подключает "DSP2833x_SWPrioritizedIsrLevels.h": поставляемое TI описание SWPrioritizedIsrLevels для семейства C2833x. */
|
||
#include "DSP2833x_SWPrioritizedIsrLevels.h"
|
||
/* Подключает "filter_bat2.h": накопительный фильтр измерительных каналов. */
|
||
#include "filter_bat2.h"
|
||
/* Подключает "measure.h": обработка датчиков, вычисление величин и миллисекундный такт системы. */
|
||
#include "measure.h"
|
||
|
||
/* Подключает "RS485.h": приём и передача SCI-A/SCI-B, упаковка команд и FIFO первого порта. */
|
||
#include "RS485.h"
|
||
/* Подключает "message.h": обработка Modbus, заводские параметры и сохранение настроек. */
|
||
#include "message.h"
|
||
/* Подключает "DAC.h": выход ЦАП и автомат защиты нагрузки с проверкой напряжения и свежести CAN. */
|
||
#include "DAC.h"
|
||
/* Подключает "package.h": адреса устройств, типы плат и псевдонимы технологических регистров. */
|
||
#include "package.h"
|
||
/* Подключает "tools.h": настройка внешней памяти и служебные задержки. */
|
||
#include "tools.h"
|
||
|
||
/* Подключает "peripher.h": перемычки режима платы, дискретные входы и выходы. */
|
||
#include "peripher.h"
|
||
/* Подключает "log_to_mem.h": двухбанковый регистратор событий нагрузки во внешней RAM. */
|
||
#include "log_to_mem.h"
|
||
|
||
/* Задаёт dCLK_1 = GpioDataRegs.GPBSET.bit.GPIO63=1; данные dCLK_1. */
|
||
#define dCLK_1() GpioDataRegs.GPBSET.bit.GPIO63=1
|
||
/* Задаёт dCLK_0 = GpioDataRegs.GPBCLEAR.bit.GPIO63=1; данные dCLK_0. */
|
||
#define dCLK_0() GpioDataRegs.GPBCLEAR.bit.GPIO63=1
|
||
/* Задаёт dCS_1 = GpioDataRegs.GPBSET.bit.GPIO59=1; данные dCS_1. */
|
||
#define dCS_1() GpioDataRegs.GPBSET.bit.GPIO59=1
|
||
/* Задаёт dCS_0 = GpioDataRegs.GPBCLEAR.bit.GPIO59=1; данные dCS_0. */
|
||
#define dCS_0() GpioDataRegs.GPBCLEAR.bit.GPIO59=1
|
||
|
||
/* Объявляет WAKEpowse — данные WAKEpowse. */
|
||
unsigned long WAKEpowse;
|
||
/* Объявляет IMPowse — данные IMPowse. */
|
||
unsigned long IMPowse;
|
||
/* Объявляет period_dac — период ЦАП; time_dac — time ЦАП. */
|
||
unsigned int period_dac, time_dac;
|
||
|
||
/* Все выходы УМП и рампа принадлежат одному обработчику Timer1.
|
||
* Основной цикл больше не выдаёт START: иначе он может увидеть короткий
|
||
* спад команды, который пропустит расчёт ЦАП с периодом 5 мс. */
|
||
/* Задаёт LOAD_IDLE = 0U; состояние ожидания команды пуска. */
|
||
#define LOAD_IDLE 0U
|
||
/* Задаёт LOAD_RUN = 1U; состояние работающей нагрузки и нарастания тока. */
|
||
#define LOAD_RUN 1U
|
||
/* Задаёт LOAD_RELOAD = 2U; состояние выдержки перед повторным запуском. */
|
||
#define LOAD_RELOAD 2U
|
||
/* Задаёт LOAD_FAULT = 3U; состояние остановки после исчерпания попыток. */
|
||
#define LOAD_FAULT 3U
|
||
/* Задаёт LOAD_VOLTAGE_MIN = 30U; минимальное разрешённое напряжение нагрузки — 30 В. */
|
||
#define LOAD_VOLTAGE_MIN 30U
|
||
/* Задаёт LOAD_PULSE_MS = 1000U; длительность удержания импульса START/STOP — 1000 мс. */
|
||
#define LOAD_PULSE_MS 1000U
|
||
/* Стартовая выдержка 3 секунды выбрана для реакции УМП и обновления ЗПТ; при DAC_FREQ=200 Гц это 600 отправок задания 6 мА. */
|
||
#define LOAD_START_WAIT_STEPS (3U * DAC_FREQ)
|
||
/* load_start_remaining хранит остаток 600 стартовых отправок 6 мА; ноль означает конец выдержки или подтверждённые 30 В. */
|
||
static unsigned int load_start_remaining;
|
||
/* Хранит состояние автомата, число выданных START в текущем сеансе и запрет повторного пуска; лимит берётся отдельно из регистра 0x7D. */
|
||
static unsigned int load_state, load_attempts, load_inhibit;
|
||
/* Объявляет load_count_dac — load число ЦАП; load_count_ramp — load число ramp; load_init_count — load init число. */
|
||
static unsigned int load_count_dac, load_count_ramp, load_init_count;
|
||
/* Объявляет load_zero_sent — данные load_zero_sent; load_stop_asserted — признак установленного выхода STOP; load_initialized — данные load_initialized. */
|
||
static unsigned int load_zero_sent, load_stop_asserted, load_initialized;
|
||
/* Объявляет load_go — начальный ток за вычетом базовых 4 мА, ограниченный диапазоном 0..5; load_end — конечный ток за вычетом 4 мА, ограниченный сверху значением 16. */
|
||
static float load_go, load_end;
|
||
/* 16-битные поля общие с CAN ISR. Снимок и увеличение возраста выполняются
|
||
* под короткой блокировкой IRQ; отправка SPI под блокировкой не выполняется. */
|
||
/* Объявляет load_voltage — последнее выбранное напряжение CAN, масштаб 1 В на слово; load_voltage_age — возраст последнего достоверного выбранного напряжения CAN в миллисекундах; load_voltage_seen — признак получения выбранного измерения напряжения CAN. volatile требует фактического чтения или записи при каждом доступе. */
|
||
static volatile unsigned int load_voltage, load_voltage_age, load_voltage_seen;
|
||
/* Объявляет load_simulation — данные load_simulation. volatile требует фактического чтения или записи при каждом доступе. */
|
||
static volatile unsigned int load_simulation;
|
||
|
||
/* Сбрасывает все переменные автомата нагрузки до разрешения прерываний; COFF не обнуляет всю .ebss. */
|
||
void Load_control_init(void)
|
||
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
|
||
{
|
||
/* Записывает load_state значением LOAD_IDLE; load_attempts = 0; load_inhibit = 0; текущее состояние автомата нагрузки. */
|
||
load_state = LOAD_IDLE; load_attempts = 0; load_inhibit = 0;
|
||
/* Начинает первую попытку без накопленной выдержки и подтверждённого роста ЗПТ; COFF требует явной инициализации статических полей. */
|
||
load_start_remaining = 0;
|
||
/* Записывает load_count_dac значением 0; load_count_ramp = 0; load_init_count = 1; load число ЦАП. */
|
||
load_count_dac = 0; load_count_ramp = 0; load_init_count = 1;
|
||
/* Записывает load_zero_sent значением 0; load_stop_asserted = 0; load_initialized = 0; данные load_zero_sent. */
|
||
load_zero_sent = 0; load_stop_asserted = 0; load_initialized = 0;
|
||
/* Записывает load_go значением 0; load_end = 0; начальный ток за вычетом базовых 4 мА, ограниченный диапазоном 0..5. */
|
||
load_go = 0; load_end = 0;
|
||
/* Записывает load_voltage значением 0; load_voltage_age = 0; load_voltage_seen = 0; последнее выбранное напряжение CAN, масштаб 1 В на слово. */
|
||
load_voltage = 0; load_voltage_age = 0; load_voltage_seen = 0;
|
||
/* Записывает load_simulation значением 0; данные load_simulation. */
|
||
load_simulation = 0;
|
||
/* Записывает IMPowse значением 0; WAKEpowse = 0; period_dac = 0; time_dac = 0; данные IMPowse. */
|
||
IMPowse = 0; WAKEpowse = 0; period_dac = 0; time_dac = 0;
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
|
||
/* Возраст сбрасывает только нужное напряжение, а не любой CAN-пакет.
|
||
* УМП 5: ЗПТ1, индекс 305; УМП 6: ЗПТ2, индекс 8. Масштаб 1 В/ед.
|
||
* Маски слотов сохраняют адреса даже для пропущенных слов кадра. */
|
||
/* Принимает напряжение выбранной нагрузки из достоверных слов CAN и сбрасывает возраст только нужного измерения. */
|
||
void Load_voltage_can(unsigned int header, unsigned int a,
|
||
/* Объявляет b — данные b. */
|
||
unsigned int b, unsigned int c)
|
||
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
|
||
{
|
||
/* Объявляет address — адрес. Начальное значение задаётся справа от знака равенства. */
|
||
unsigned int address = header & 0x1FFFU;
|
||
/* Объявляет selected — данные selected; i — данные i; mask — маска; data — данные. Начальное значение задаётся справа от знака равенства. */
|
||
unsigned int selected, i, mask = 0x8000U, data[3];
|
||
/* Проверяет if (Desk != dsk_LOAD или (load_simulation & 1U)) return; последующий оператор выполняется только при истинном условии. */
|
||
if (Desk != dsk_LOAD || (load_simulation & 1U)) return;
|
||
/* Проверяет if (Mode == adr_LOA1) selected = 305U; последующий оператор выполняется только при истинном условии. */
|
||
if (Mode == adr_LOA1) selected = 305U;
|
||
/* Проверяет else if (Mode == adr_LOA2) selected = 8U; последующий оператор выполняется только при истинном условии. */
|
||
else if (Mode == adr_LOA2) selected = 8U;
|
||
/* Выполняет альтернативу, если предыдущее условие if оказалось ложным. */
|
||
else return;
|
||
/* Записывает data[0] значением a; data[1] = b; data[2] = c; данные. */
|
||
data[0] = a; data[1] = b; data[2] = c;
|
||
/* Цикл for (i = 0; i < 3; i++, address++, mask >>= 1) ; задаёт начальное значение, условие продолжения и шаг перебора. */
|
||
for (i = 0; i < 3; i++, address++, mask >>= 1) {
|
||
/* Проверяет if ((header & mask) и address == selected); последующий оператор выполняется только при истинном условии. */
|
||
if ((header & mask) && address == selected) {
|
||
/* Записывает load_voltage значением data[i]; последнее выбранное напряжение CAN, масштаб 1 В на слово. */
|
||
load_voltage = data[i];
|
||
/* Записывает load_voltage_age значением 0; возраст последнего достоверного выбранного напряжения CAN в миллисекундах. */
|
||
load_voltage_age = 0;
|
||
/* Записывает load_voltage_seen значением 1; признак получения выбранного измерения напряжения CAN. */
|
||
load_voltage_seen = 1;
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
|
||
/* STOP удерживается весь сброс, включая отправку кода 4 мА в ЦАП.
|
||
* Повторный пуск разрешён только после этой отправки и 1000 мс STOP.
|
||
* Это подтверждение программной отправки, не измерение физического тока. */
|
||
/* Снимает START и разрешение нагрузки, удерживает STOP 1000 мс и сбрасывает состояние нарастания тока. */
|
||
static void load_stop(unsigned int state)
|
||
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
|
||
{
|
||
/* Сбрасывает аппаратный сигнал через clear_START_DPTB();. */
|
||
clear_START_DPTB();
|
||
/* Устанавливает аппаратный сигнал через set_STOP_DPTB();. */
|
||
set_STOP_DPTB();
|
||
/* Сбрасывает аппаратный сигнал через clear_RES_OUT_2();. */
|
||
clear_RES_OUT_2();
|
||
/* Записывает load_stop_asserted значением 1; признак установленного выхода STOP. */
|
||
load_stop_asserted = 1;
|
||
/* Записывает load_state значением state; текущее состояние автомата нагрузки. */
|
||
load_state = state;
|
||
/* Записывает load_count_ramp значением 0; load число ramp. */
|
||
load_count_ramp = 0;
|
||
/* Записывает load_zero_sent значением 0; данные load_zero_sent. */
|
||
load_zero_sent = 0;
|
||
/* Записывает IMPowse значением LOAD_PULSE_MS; данные IMPowse. */
|
||
IMPowse = LOAD_PULSE_MS;
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
|
||
/* Ограничивает начальный и конечный ток, снимает STOP, выдаёт START и считает новую попытку пуска. */
|
||
static void load_start(void)
|
||
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
|
||
{
|
||
/* Уставки фиксируются для каждой попытки. Внутренний ноль = 4 мА.
|
||
* Исправлена опечатка старого ограничения: ограничиваем конец рампы,
|
||
* а не заменяем начальную уставку на 16 (что давало скачок до 20 мА). */
|
||
/* Записывает load_go значением (float)DAC_go - 4.0; начальный ток за вычетом базовых 4 мА, ограниченный диапазоном 0..5. */
|
||
load_go = (float)DAC_go - 4.0;
|
||
/* Записывает load_end значением (float)DAC_stop - 4.0; конечный ток за вычетом 4 мА, ограниченный сверху значением 16. */
|
||
load_end = (float)DAC_stop - 4.0;
|
||
/* Проверяет if (load_go < 0) load_go = 0; последующий оператор выполняется только при истинном условии. */
|
||
if (load_go < 0) load_go = 0;
|
||
/* Проверяет if (load_go > 5) load_go = 5; последующий оператор выполняется только при истинном условии. */
|
||
if (load_go > 5) load_go = 5;
|
||
/* Проверяет if (load_end > 16) load_end = 16; последующий оператор выполняется только при истинном условии. */
|
||
if (load_end > 16) load_end = 16;
|
||
/* Проверяет if (load_end <= load_go) load_end = load_go + 1; последующий оператор выполняется только при истинном условии. */
|
||
if (load_end <= load_go) load_end = load_go + 1;
|
||
/* Каждая попытка получает 600 токовых тактов, то есть 3 секунды при 200 Гц; остаток уменьшается только после отправки 6 мА. */
|
||
load_start_remaining = LOAD_START_WAIT_STEPS;
|
||
/* Записывает load_count_ramp значением 0; load число ramp. */
|
||
load_count_ramp = 0;
|
||
/* Изменяет счётчик в выражении load_attempts++; на один для следующего шага. */
|
||
load_attempts++;
|
||
/* STOP отпускаем непосредственно перед START, разрешение — после него.
|
||
* Задание остаётся 4 мА до первого шага новой рампы. */
|
||
/* Сбрасывает аппаратный сигнал через clear_STOP_DPTB();. */
|
||
clear_STOP_DPTB();
|
||
/* Записывает load_stop_asserted значением 0; признак установленного выхода STOP. */
|
||
load_stop_asserted = 0;
|
||
/* Устанавливает аппаратный сигнал через set_START_DPTB();. */
|
||
set_START_DPTB();
|
||
/* Устанавливает аппаратный сигнал через set_RES_OUT_2();. */
|
||
set_RES_OUT_2();
|
||
/* Записывает IMPowse значением LOAD_PULSE_MS; данные IMPowse. */
|
||
IMPowse = LOAD_PULSE_MS;
|
||
/* Записывает load_state значением LOAD_RUN; текущее состояние автомата нагрузки. */
|
||
load_state = LOAD_RUN;
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
|
||
/* Вычисляет такты ЦАП; для недопустимого сохранённого тайм-аута CAN возвращает 600 мс. */
|
||
void Setup_DAC_time(void)
|
||
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
|
||
{
|
||
/* Записывает WAKEpowse значением 3L * DAC_FREQ +1; данные WAKEpowse. */
|
||
WAKEpowse = 3L * DAC_FREQ +1;
|
||
|
||
/* Записывает period_dac значением READY_FREQ / DAC_FREQ; период ЦАП. */
|
||
period_dac = READY_FREQ / DAC_FREQ;
|
||
/* Записывает time_dac значением LOAD_TIME * DAC_FREQ; time ЦАП. */
|
||
time_dac = LOAD_TIME * DAC_FREQ;
|
||
/* Старые EEPROM имеют ноль в новом параметре: используем 600 мс.
|
||
* 1..60000 мс можно менять через регистр 0x52 и сохранять штатно. */
|
||
/* Проверяет if ((unsigned int)LOAD_CAN_TIMEOUT == 0U или; последующий оператор выполняется только при истинном условии. */
|
||
if ((unsigned int)LOAD_CAN_TIMEOUT == 0U ||
|
||
/* Записывает LOAD_CAN_TIMEOUT значением 600; сохраняемый тайм-аут свежести напряжения CAN в миллисекундах, регистр 0x52. */
|
||
(unsigned int)LOAD_CAN_TIMEOUT > 60000U) LOAD_CAN_TIMEOUT = 600;
|
||
/* Старый нулевой счётчик в EEPROM заменяется заводским лимитом пяти пусков; ненулевую сохранённую настройку 0x7D не стираем. */
|
||
if (!(unsigned int)LOAD_MAX_ATTEMPTS) LOAD_MAX_ATTEMPTS = LOAD_DEFAULT_MAX_ATTEMPTS;
|
||
/* Сбрасывает диагностическое слово 0x7C при подготовке автомата; сохраняемый лимит не является текущим счётчиком. */
|
||
LOAD_STATUS = 0;
|
||
/* Записывает load_initialized значением 1; данные load_initialized. */
|
||
load_initialized = 1;
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
|
||
/* Начинает функцию dOUT; назначение модуля — выход ЦАП и автомат защиты нагрузки с проверкой напряжения и свежести CAN. */
|
||
void dOUT(int x)
|
||
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
|
||
{
|
||
/* Проверяет if(x) GpioDataRegs.GPBSET.bit.GPIO60=1; последующий оператор выполняется только при истинном условии. */
|
||
if(x) GpioDataRegs.GPBSET.bit.GPIO60=1;
|
||
/* Выполняет альтернативу, если предыдущее условие if оказалось ложным. */
|
||
else GpioDataRegs.GPBCLEAR.bit.GPIO60=1;
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
|
||
/* Начинает функцию wast; назначение модуля — выход ЦАП и автомат защиты нагрузки с проверкой напряжения и свежести CAN. */
|
||
void wast()
|
||
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
|
||
{
|
||
/* Объявляет i — данные i. */
|
||
int i;
|
||
|
||
/* Цикл for(i=0;i<25;i++);; задаёт начальное значение, условие продолжения и шаг перебора. */
|
||
for(i=0;i<25;i++);
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
|
||
/* Начинает функцию dSEND; назначение модуля — выход ЦАП и автомат защиты нагрузки с проверкой напряжения и свежести CAN. */
|
||
void dSEND(unsigned int word)
|
||
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
|
||
{
|
||
/* Объявляет i — данные i. */
|
||
int i;
|
||
|
||
/* Вызывает dCS_0 с аргументами (); DSP28x_usDelay(1L) в текущей последовательности операций. */
|
||
dCS_0(); DSP28x_usDelay(1L);
|
||
|
||
/* Цикл for(i=0;i<16;i++); задаёт начальное значение, условие продолжения и шаг перебора. */
|
||
for(i=0;i<16;i++)
|
||
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
|
||
{
|
||
/* Вызывает dCLK_1 с аргументами () в текущей последовательности операций. */
|
||
dCLK_1();
|
||
/* Записывает word значением word<<1; DSP28x_usDelay(1L); слово. */
|
||
dOUT((word & 0x8000)?1:0); word = word<<1; DSP28x_usDelay(1L);
|
||
/* Вызывает dCLK_0 с аргументами (); DSP28x_usDelay(1L) в текущей последовательности операций. */
|
||
dCLK_0(); DSP28x_usDelay(1L);
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
/* Вызывает dCS_1 с аргументами () в текущей последовательности операций. */
|
||
dCS_1();
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
|
||
/* Начинает функцию Init_DAC; назначение модуля — выход ЦАП и автомат защиты нагрузки с проверкой напряжения и свежести CAN. */
|
||
void Init_DAC(void)
|
||
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
|
||
{
|
||
/* Вызывает dSEND с аргументами (0x9002|0x0000) в текущей последовательности операций. */
|
||
dSEND(0x9002|0x0000);
|
||
/* Переключает аппаратный сигнал через toggle_RES_OUT_1();. */
|
||
toggle_RES_OUT_1();
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
|
||
/* Начинает функцию anal_output; назначение модуля — выход ЦАП и автомат защиты нагрузки с проверкой напряжения и свежести CAN. */
|
||
static void anal_output(long vrot, long maxx, int calibration)
|
||
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
|
||
{
|
||
/* Объявляет out — данные out. */
|
||
long out;
|
||
/* Объявляет x — данные x. */
|
||
static int x;
|
||
|
||
/* Линейно переводит долю рампы vrot/maxx в код между DAC_min() и DAC_max(); целочисленное деление каждого слагаемого задаёт округление. */
|
||
out = DAC_min() + vrot * DAC_max() / maxx - vrot * DAC_min() / maxx ;
|
||
|
||
/* Фиксирует выбор нижней ветви передачи: при x=0 далее используется управляющее слово out|0x8000. */
|
||
x=0;// !x; тщетно бытие
|
||
|
||
/* Проверяет if(calibration и cCalibrDac) out=DAC_cal(); последующий оператор выполняется только при истинном условии. */
|
||
if(calibration && cCalibrDac) out=DAC_cal();
|
||
|
||
/* Оставляет младшие 12 бит кода ЦАП. Это усечение битов, а не насыщение на границах 0..4095. */
|
||
out = out & 0xFFF;
|
||
|
||
/* Проверяет if(x) dSEND(out|0x0000|0x0000); последующий оператор выполняется только при истинном условии. */
|
||
if(x) dSEND(out|0x0000|0x0000);
|
||
/* Выполняет альтернативу, если предыдущее условие if оказалось ложным. */
|
||
else dSEND(out|0x8000|0x0000);
|
||
|
||
/* Публикует фактически отправляемый 12-битный код ЦАП в технологическом регистре 0x7B для диагностики и регистрации. */
|
||
modbus[123] = out;
|
||
|
||
/* Публикует расчётный выход 4..20 мА в масштабе 0,1 мА на единицу: при доле рампы 0 значение 40, при доле 1 значение 200. */
|
||
modbus[0x1C] = 40L + vrot * 200 / maxx - vrot * 40 / maxx ;
|
||
|
||
/*
|
||
if(!no_write)
|
||
{
|
||
Test_mem_limit(4);
|
||
|
||
Log_to_mem(modbus[0x7B]);
|
||
Log_to_mem(modbus[0x63]);
|
||
Log_to_mem(modbus[0x1C]);
|
||
Log_to_mem(modbus[0x1D]);
|
||
|
||
}
|
||
*/
|
||
|
||
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
|
||
/* Сохранён публичный интерфейс; защитный сброс вызывает внутреннюю
|
||
* функцию с calibration=0, чтобы ручная калибровка не подменила 4 мА. */
|
||
/* Начинает функцию Anal_output; назначение модуля — выход ЦАП и автомат защиты нагрузки с проверкой напряжения и свежести CAN. */
|
||
void Anal_output(long vrot, long maxx)
|
||
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
|
||
{
|
||
/* Проверяет if (maxx > 0) anal_output(vrot, maxx, 1); последующий оператор выполняется только при истинном условии. */
|
||
if (maxx > 0) anal_output(vrot, maxx, 1);
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
|
||
/* В такте Timer1 проверяет команды, напряжение и давность CAN; управляет STOP, повторным пуском и током ЦАП. */
|
||
void Load_runner(void)
|
||
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
|
||
{
|
||
/* Хранит состояние IRQ, снимки команд и напряжения, свежесть CAN, тайм-аут в мс и лимит пусков; 16-битный лимит читается один раз за такт. */
|
||
unsigned int saved, commands, voltage, fresh, timeout, attempt_limit;
|
||
/* start/reset хранят командные биты; active отмечает работу; voltage_wait отмечает вывод 6 мА; ramp_before позволяет заморозить положение рампы. */
|
||
unsigned int start, reset, active = 0, voltage_wait = 0, ramp_before;
|
||
/* Объявляет sinus — данные sinus. Начальное значение задаётся справа от знака равенства. */
|
||
float sinus = 0;
|
||
|
||
/* ISR работает 1 кГц, но GPIO/ЦАП нельзя обслуживать до загрузки
|
||
* параметров. Это также исключает прежнее деление на time_dac=0. */
|
||
/* Проверяет if (!load_initialized или !READY или !period_dac или !time_dac) return; последующий оператор выполняется только при истинном условии. */
|
||
if (!load_initialized || !READY || !period_dac || !time_dac) return;
|
||
/* Записывает saved значением __disable_interrupts(); данные saved. */
|
||
saved = __disable_interrupts();
|
||
/* Записывает commands значением (unsigned int)Commands; данные commands. */
|
||
commands = (unsigned int)Commands;
|
||
/* Записывает voltage значением (load_simulation & 2U) ? 0U : load_voltage; данные voltage. */
|
||
voltage = (load_simulation & 2U) ? 0U : load_voltage;
|
||
/* Записывает timeout значением (unsigned int)LOAD_CAN_TIMEOUT; данные timeout. */
|
||
timeout = (unsigned int)LOAD_CAN_TIMEOUT;
|
||
/* Читает 0x7D как беззнаковое 16-битное число, чтобы значения 32768..65535 сохраняли положительный смысл лимита. */
|
||
attempt_limit = (unsigned int)LOAD_MAX_ATTEMPTS;
|
||
/* При нештатном нуле в RAM оставляет прежний предел пяти пусков; штатные обработчики записи ноль отклоняют. */
|
||
if (!attempt_limit) attempt_limit = LOAD_DEFAULT_MAX_ATTEMPTS;
|
||
/* Проверяет if (!timeout или timeout > 60000U) timeout = 600U; последующий оператор выполняется только при истинном условии. */
|
||
if (!timeout || timeout > 60000U) timeout = 600U;
|
||
/* До первого пакета значение 0 не считается свежим измерением.
|
||
* Насыщение возраста исключает ложное восстановление после переполнения. */
|
||
/* Записывает fresh значением load_voltage_seen && load_voltage_age < timeout; данные fresh. */
|
||
fresh = load_voltage_seen && load_voltage_age < timeout;
|
||
/* Проверяет if (load_voltage_age < 65535U) load_voltage_age++; последующий оператор выполняется только при истинном условии. */
|
||
if (load_voltage_age < 65535U) load_voltage_age++;
|
||
/* Вызывает __restore_interrupts с аргументами (saved) в текущей последовательности операций. */
|
||
__restore_interrupts(saved);
|
||
/* Записывает start значением commands & 0x80U; reset = commands & 0x40U; данные start. */
|
||
start = commands & 0x80U; reset = commands & 0x40U;
|
||
/* Проверяет if (IMPowse) IMPowse--; последующий оператор выполняется только при истинном условии. */
|
||
if (IMPowse) IMPowse--;
|
||
|
||
/* Отсутствие команды пуска — начало нового сеанса, сбрасывает ошибку.
|
||
* RESET всегда старше START; отпускание RESET при оставшемся START
|
||
* не даёт неожиданного повторного пуска: сначала требуется START=0. */
|
||
/* Проверяет if (!start); последующий оператор выполняется только при истинном условии. */
|
||
if (!start) {
|
||
/* Проверяет if (load_state != LOAD_IDLE) load_stop(LOAD_IDLE); последующий оператор выполняется только при истинном условии. */
|
||
if (load_state != LOAD_IDLE) load_stop(LOAD_IDLE);
|
||
/* Снятие START обнуляет счётчик текущего сеанса и запрет повторного пуска, сохраняя настроенный лимит в 0x7D. */
|
||
load_attempts = 0; load_inhibit = 0;
|
||
/* Проверяет else if (reset); последующий оператор выполняется только при истинном условии. */
|
||
} else if (reset) {
|
||
/* Проверяет if (load_state != LOAD_FAULT и load_state != LOAD_IDLE); последующий оператор выполняется только при истинном условии. */
|
||
if (load_state != LOAD_FAULT && load_state != LOAD_IDLE)
|
||
/* Вызывает load_stop. Снимает START и разрешение нагрузки, удерживает STOP 1000 мс и сбрасывает состояние нарастания тока. */
|
||
load_stop(LOAD_IDLE);
|
||
/* Записывает load_inhibit значением 1; признак запрета повторного запуска до требуемого перехода входов. */
|
||
load_inhibit = 1;
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
/* Проверяет if (reset и !load_stop_asserted) load_stop(LOAD_IDLE); последующий оператор выполняется только при истинном условии. */
|
||
if (reset && !load_stop_asserted) load_stop(LOAD_IDLE);
|
||
|
||
/* LOAD_RELOAD уже удерживает STOP, 4 мА и снятое разрешение; уменьшение лимита только фиксирует ошибку, не перезапуская секундную выдержку. */
|
||
if (load_state == LOAD_RELOAD && load_attempts >= attempt_limit) load_state = LOAD_FAULT;
|
||
/* Повторный START допускается после сброса ЦАП, окончания STOP и
|
||
* восстановления нужного CAN-напряжения. Таймаут сам не тратит попытку. */
|
||
/* Команда START без RESET и запрета должна дождаться окончания инициализации, свежего ЗПТ и подтверждённого задания 4 мА. */
|
||
if (start && !reset && !load_inhibit && !WAKEpowse && fresh &&
|
||
/* Секундный STOP заканчивается только после передачи исходного кода ЦАП; обе выдержки обязательны перед пуском. */
|
||
load_zero_sent && !IMPowse &&
|
||
/* LOAD_IDLE начинает сеанс с нулевым счётчиком, а LOAD_RELOAD проверен на оставшиеся попытки выше; ошибка не разрешает START. */
|
||
(load_state == LOAD_IDLE || load_state == LOAD_RELOAD)) load_start();
|
||
/* Проверяет if (load_state == LOAD_RUN и !IMPowse) clear_START_DPTB(); последующий оператор выполняется только при истинном условии. */
|
||
if (load_state == LOAD_RUN && !IMPowse) clear_START_DPTB();
|
||
|
||
/* Проверяет if (++load_count_dac >= period_dac); последующий оператор выполняется только при истинном условии. */
|
||
if (++load_count_dac >= period_dac) {
|
||
/* Записывает load_count_dac значением 0; load число ЦАП. */
|
||
load_count_dac = 0;
|
||
/* Проверяет if (WAKEpowse); последующий оператор выполняется только при истинном условии. */
|
||
if (WAKEpowse) {
|
||
/* Элемент постоянной таблицы --WAKEpowse;; порядок значений совпадает с порядком индексов потребителя. */
|
||
--WAKEpowse;
|
||
/* Проверяет if (WAKEpowse и !(WAKEpowse % (DAC_FREQ / 2))); последующий оператор выполняется только при истинном условии. */
|
||
if (WAKEpowse && !(WAKEpowse % (DAC_FREQ / 2))) {
|
||
/* Записывает load_init_count значением 1; load init число. */
|
||
Init_DAC(); load_init_count = 1;
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
/* Выполняет альтернативу, если предыдущее условие if оказалось ложным. */
|
||
} else {
|
||
/* Записывает active значением load_state == LOAD_RUN; данные active. */
|
||
active = load_state == LOAD_RUN;
|
||
/* Проверяет if (active); последующий оператор выполняется только при истинном условии. */
|
||
if (active) {
|
||
/* В паузе CAN не вычисляет следующий шаг: положение, код и остаток стартовой выдержки сохраняются. */
|
||
if (fresh) {
|
||
/* Сохраняет положение до шага, чтобы отменить рост на время начального ожидания при 6 мА. */
|
||
ramp_before = load_count_ramp;
|
||
/* Во второй половине штатной рампы сохраняет прежнее ускорение на два отсчёта за токовый такт. */
|
||
if (load_count_ramp > time_dac / 2) load_count_ramp++;
|
||
/* Продвигает рампу на один отсчёт и ограничивает её концом, заданным LOAD_TIME и DAC_FREQ. */
|
||
if (++load_count_ramp > time_dac) load_count_ramp = time_dac;
|
||
/* Преобразует продвижение рампы в долю полного токового диапазона 16 мА с учётом начальной уставки. */
|
||
sinus = ((load_end - load_go) / 16.0 * load_count_ramp) +
|
||
/* Добавляет начальную уставку относительно базовых 4 мА в тех же единицах положения рампы. */
|
||
(load_go / 16.0 * time_dac);
|
||
/* Подтверждённые 30 В завершают стартовое ожидание; последующее падение ЗПТ не получает новую выдержку. */
|
||
if (voltage >= LOAD_VOLTAGE_MIN) load_start_remaining = 0;
|
||
/* При меньшем ЗПТ внутренние 2 мА поверх базовых 4 мА ограничивают следующее задание суммарными 6 мА. */
|
||
else if (sinus * 16.0 > 2.0 * time_dac) {
|
||
/* До подтверждения порога даёт попытке остаток выбранных 3 секунд, не отправляя повышенный ток. */
|
||
if (load_start_remaining) {
|
||
/* Отменяет шаг рампы во время ожидания, чтобы после роста ЗПТ продолжить с прежнего положения. */
|
||
load_count_ramp = ramp_before;
|
||
/* Доля 1/8 диапазона 16 мА добавляет 2 мА к базовым 4 мА; time_dac=6000 даёт положение 750. */
|
||
sinus = time_dac / 8.0;
|
||
/* Отсчитывает выдержку только после отправки задания; служебная инициализация ЦАП не расходует её. */
|
||
voltage_wait = 1;
|
||
/* Исчерпанная выдержка или падение напряжения после успешного роста требуют немедленного STOP. */
|
||
} else {
|
||
/* Возвращает 4 мА и снимает разрешение; исчерпание сохранённого лимита попыток запрещает следующий пуск. */
|
||
load_stop(load_attempts >= attempt_limit ?
|
||
/* Использует согласованный снимок лимита текущего такта; заводские пять пусков сохраняют прежний предел. */
|
||
LOAD_FAULT : LOAD_RELOAD);
|
||
/* Исключает отправку рабочего задания в такте защитной остановки. */
|
||
active = 0; sinus = 0;
|
||
/* Закрывает выбор между ожиданием и защитным сбросом. */
|
||
}
|
||
/* Завершает проверку следующего токового задания при низком ЗПТ. */
|
||
}
|
||
/* Завершает обработку активной рампы при свежем выбранном напряжении CAN. */
|
||
}
|
||
/* Выполняет альтернативу, если предыдущее условие if оказалось ложным. */
|
||
} else {
|
||
/* Записывает load_count_ramp значением 0; load число ramp. */
|
||
load_count_ramp = 0;
|
||
/* Записывает load_init_count значением (load_init_count + 1) & 0x3FFU; load init число. */
|
||
load_init_count = (load_init_count + 1) & 0x3FFU;
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
/* Во время защиты и пуска ручной код калибровки запрещён.
|
||
* Калибровка сохранена только при снятом START и свободном STOP.
|
||
* Нулевой код задания здесь означает ток 4 мА по DAC_min(). */
|
||
/* Проверяет if (active и !fresh); последующий оператор выполняется только при истинном условии. */
|
||
if (active && !fresh) {
|
||
/* Удерживаем ЦАП. Даже cInitDac не меняет ток в паузе CAN. */
|
||
/* Проверяет else if ((!load_init_count или cInitDac) и !load_stop_asserted); последующий оператор выполняется только при истинном условии. */
|
||
} else if ((!load_init_count || cInitDac) && !load_stop_asserted) {
|
||
/* Записывает load_init_count значением 1; cInitDac = 0; load init число. */
|
||
Init_DAC(); load_init_count = 1; cInitDac = 0;
|
||
/* Выполняет альтернативу, если предыдущее условие if оказалось ложным. */
|
||
} else {
|
||
/* Вызывает anal_output с аргументами ((int)sinus, time_dac, в текущей последовательности операций. */
|
||
anal_output((int)sinus, time_dac,
|
||
/* Продолжает список аргументов, полей или выражение !start && !load_stop_asserted);; порядок элементов сохраняется. */
|
||
!start && !load_stop_asserted);
|
||
/* Уменьшает остаток только после отправки 6 мА: тайм-аут CAN, инициализация ЦАП и STOP не расходуют выдержку. */
|
||
if (voltage_wait) load_start_remaining--;
|
||
/* A manual calibration frame is not a confirmed 4 mA reload.
|
||
* START must wait for a forced baseline frame after it. */
|
||
/* Проверяет if (!active) load_zero_sent = !(!start и !load_stop_asserted и cCalibrDac); последующий оператор выполняется только при истинном условии. */
|
||
if (!active) load_zero_sent = !(!start && !load_stop_asserted && cCalibrDac);
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
/* Диагностика доступна обычным Modbus/CAN, независимо от логгера:
|
||
* бит 0 — ошибка пяти пусков; бит 1 — нет свежего напряжения;
|
||
* бит 2 — сброс перед повтором; бит 3 — работа рампы.
|
||
* Ошибка удерживается со STOP, пока оператор не снимет START. */
|
||
/* Записывает LOAD_STATUS значением (load_state == LOAD_FAULT ? 1U : 0U) |; слово состояния защиты нагрузки для чтения, регистр 0x7C. */
|
||
LOAD_STATUS = (load_state == LOAD_FAULT ? 1U : 0U) |
|
||
/* Продолжает список аргументов, полей или выражение (!fresh ? 2U : 0U) |; порядок элементов сохраняется. */
|
||
(!fresh ? 2U : 0U) |
|
||
/* Продолжает список аргументов, полей или выражение (load_state == LOAD_RELOAD ? 4U : 0U) |; порядок элементов сохраняется. */
|
||
(load_state == LOAD_RELOAD ? 4U : 0U) |
|
||
/* Продолжает список аргументов, полей или выражение (load_state == LOAD_RUN ? 8U : 0U) |; порядок элементов сохраняется. */
|
||
(load_state == LOAD_RUN ? 8U : 0U) |
|
||
/* Продолжает список аргументов, полей или выражение (load_simulation << 4);; порядок элементов сохраняется. */
|
||
(load_simulation << 4);
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
|
||
/* Local versioned block, shared by RTU and service CAN. Probe its signature
|
||
* before writes: old firmware returns exception 2 instead of starting tests.
|
||
* Simulation is RAM-only, zeroed at boot, and never copied into EEPROM.
|
||
* bit 0 ignores ZPT reception (age reaches the configured timeout);
|
||
* bit 1 substitutes 0 V only for protection, leaving real logged CAN intact. */
|
||
/* Фиксирует управляющие входы перед выполнением автомата защиты нагрузки. */
|
||
unsigned int Load_protection_prepare(unsigned int function, unsigned int address,
|
||
/* Объявляет value — данные value; out — данные out. Хранит указатель на данные. */
|
||
unsigned int value, unsigned int *out, unsigned int *error)
|
||
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
|
||
{
|
||
/* Объявляет saved — данные saved. Начальное значение задаётся справа от знака равенства. */
|
||
unsigned int saved = __disable_interrupts();
|
||
/* Записывает error значением 2; ошибка. */
|
||
*error = 2;
|
||
/* Проверяет if (Desk == dsk_LOAD и address == 0x1300U и function == 3 и value == 8); последующий оператор выполняется только при истинном условии. */
|
||
if (Desk == dsk_LOAD && address == 0x1300U && function == 3 && value == 8) {
|
||
/* Записывает out[0] значением 0x5550U; out[1] = 1; данные out. */
|
||
out[0] = 0x5550U; out[1] = 1;
|
||
/* Записывает out[2] значением load_simulation; out[3] = (unsigned int)LOAD_CAN_TIMEOUT; данные out. */
|
||
out[2] = load_simulation; out[3] = (unsigned int)LOAD_CAN_TIMEOUT;
|
||
/* Записывает out[4] значением (unsigned int)LOAD_STATUS; out[5] = load_attempts; данные out. */
|
||
out[4] = (unsigned int)LOAD_STATUS; out[5] = load_attempts;
|
||
/* Записывает out[6] значением load_voltage; out[7] = load_voltage_seen ? load_voltage_age : 65535U; данные out. */
|
||
out[6] = load_voltage; out[7] = load_voltage_seen ? load_voltage_age : 65535U;
|
||
/* Записывает error значением 0; ошибка. */
|
||
*error = 0;
|
||
/* Вызывает __restore_interrupts с аргументами (saved) в текущей последовательности операций. */
|
||
__restore_interrupts(saved);
|
||
/* Завершает Load_protection_prepare и возвращает 8. */
|
||
return 8;
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
/* Проверяет if (Desk == dsk_LOAD и function == 6); последующий оператор выполняется только при истинном условии. */
|
||
if (Desk == dsk_LOAD && function == 6) {
|
||
/* Проверяет if (address == 0x1302U и value <= 3U); последующий оператор выполняется только при истинном условии. */
|
||
if (address == 0x1302U && value <= 3U) {
|
||
/* Записывает load_simulation значением value; данные load_simulation. */
|
||
load_simulation = value;
|
||
/* Записывает error значением 0; ошибка. */
|
||
*error = 0;
|
||
/* Проверяет else if (address == 0x1303U и value >= 1U и value <= 60000U); последующий оператор выполняется только при истинном условии. */
|
||
} else if (address == 0x1303U && value >= 1U && value <= 60000U) {
|
||
/* Записывает LOAD_CAN_TIMEOUT значением value; *error = 0; сохраняемый тайм-аут свежести напряжения CAN в миллисекундах, регистр 0x52. */
|
||
LOAD_CAN_TIMEOUT = value; *error = 0;
|
||
/* Проверяет else if (address == 0x1302U или address == 0x1303U) *error = 3; последующий оператор выполняется только при истинном условии. */
|
||
} else if (address == 0x1302U || address == 0x1303U) *error = 3;
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|
||
/* Записывает out[0] значением address; out[1] = value; данные out. */
|
||
out[0] = address; out[1] = value;
|
||
/* Вызывает __restore_interrupts с аргументами (saved) в текущей последовательности операций. */
|
||
__restore_interrupts(saved);
|
||
/* Завершает Load_protection_prepare и возвращает 2. */
|
||
return 2;
|
||
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
|
||
}
|