/* Выход ЦАП и автомат защиты нагрузки с проверкой напряжения и свежести 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; /* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */ }