Files
Balsam_167/Source/Internal/DAC.c
Andrey 1e911e5250 Обновить логгер УМП и исправить запуск UART и EEPROM
Перейти на компактные 12-словные записи v4 и сохранять 10 процентов банка после отключения. Добавить трёхсекундное ожидание роста ЗПТ и настраиваемый лимит пусков. Явно инициализировать состояние UART и гарантировать публикацию параметров EEPROM перед запуском Timer2. Сохранить воспроизводимые сборки, регрессионные проверки, документацию и обновлённые результаты сборки; исключить временные артефакты.
2026-10-12 12:25:41 +03:00

600 lines
55 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Выход ЦАП и автомат защиты нагрузки с проверкой напряжения и свежести 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;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}