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

492 lines
46 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.
/* Двухбанковый регистратор событий нагрузки во внешней RAM. Русские пояснения сохранены в UTF-8. */
/* Подключает "DSP2833x_Device.h": поставляемое TI описание Device для семейства C2833x. */
#include "DSP2833x_Device.h"
/* Подключает "filter_bat2.h": накопительный фильтр измерительных каналов. */
#include "filter_bat2.h"
/* Подключает "measure.h": обработка датчиков, вычисление величин и миллисекундный такт системы. */
#include "measure.h"
/* Подключает "package.h": адреса устройств, типы плат и псевдонимы технологических регистров. */
#include "package.h"
/* Подключает "RS485.h": приём и передача SCI-A/SCI-B, упаковка команд и FIFO первого порта. */
#include "RS485.h"
/* Подключает "message.h": обработка Modbus, заводские параметры и сохранение настроек. */
#include "message.h"
/* Подключает "peripher.h": перемычки режима платы, дискретные входы и выходы. */
#include "peripher.h"
/* Подключает "DAC.h": выход ЦАП и автомат защиты нагрузки с проверкой напряжения и свежести CAN. */
#include "DAC.h"
/* Подключает "ADC.h": настройка АЦП, обработка преобразований и первичная фильтрация измерений. */
#include "ADC.h"
/* Подключает "log_to_mem.h": двухбанковый регистратор событий нагрузки во внешней RAM. */
#include "log_to_mem.h"
/* Объявляет no_write — no запись. Начальное значение задаётся справа от знака равенства. */
int no_write = 0,
/* Записывает never_write значением 0; never запись. */
never_write = 0; // Флаги, чтобы не писать (если что)
/* Указание компилятору DATA_SECTION(logs_block,".logg");: размещение данных или настройка обработки объявлений. */
#pragma DATA_SECTION(logs_block,".logg");
/* Объявляет logs_block — выделенный массив внешней RAM для двух банков регистратора. */
unsigned int logs_block[0xFA00];
/* Объявляет Log — данные Log. */
LOG Log;
/* Объявляет flog — данные flog. Начальное значение задаётся справа от знака равенства. */
unsigned int flog=0;
// Очищение памати, где логи лежат
/* Обнуляет выделенный диапазон внешней RAM журнала и задаёт границы кольцевого буфера. */
void clear_mem()
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет i — данные i. */
unsigned long i;
/* Записывает Log.Start значением LOG_PAGE_START; данные Start. */
Log.Start = LOG_PAGE_START;
/* Записывает Log.Finis значением LOG_PAGE_START + LOG_PAGE_LEN; данные Finis. */
Log.Finis = LOG_PAGE_START + LOG_PAGE_LEN;
/* Записывает Log.Adres значением Log.Start; данные Adres. */
Log.Adres = Log.Start;
/* Записывает Log.Circl значением 0; данные Circl. */
Log.Circl = 0;
/* Цикл for (i=Log.Start; i<Log.Finis; i++); задаёт начальное значение, условие продолжения и шаг перебора. */
for (i=Log.Start; i<Log.Finis; i++)
/* Записывает i значением 0; данные i. */
*(volatile int *)i = 0;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Two RAM banks: freeze after 10% of bank capacity is recorded after stop. */
/* Объявляет ump_state — данные ump_state; ump_count — число сохранённых точек в активном банке; ump_cursor — данные ump_cursor; ump_next — данные ump_next. volatile требует фактического чтения или записи при каждом доступе. */
static volatile unsigned int ump_state, ump_count, ump_cursor, ump_next;
/* Объявляет ump_period — период записи регистратора в миллисекундных тактах; ump_divider — ump делитель. volatile требует фактического чтения или записи при каждом доступе. Начальное значение задаётся справа от знака равенства. */
static volatile unsigned int ump_period = 20, ump_divider;
/* Объявляет ump_time — миллисекундное время регистратора; ump_sequence — ump последовательность. volatile требует фактического чтения или записи при каждом доступе. */
static volatile unsigned long ump_time, ump_sequence;
/* Объявляет ump_bank — индекс активного банка регистратора; ump_archive_bank — индекс замороженного банка, доступного для чтения. Начальное значение задаётся справа от знака равенства. */
static unsigned int ump_bank, ump_archive_bank = 1;
/* Объявляет ump_archive_count — ump archive число; ump_archive_next — данные ump_archive_next; ump_generation — номер смены архивного банка. */
static unsigned int ump_archive_count, ump_archive_next, ump_generation;
/* Объявляет ump_source — данные ump_source; ump_post — данные ump_post. */
static unsigned int ump_source, ump_post;
/* Задаёт UMP_POST_POINTS = (UMP_LOG_CAPACITY / 10U); число записей после STOP — десятая часть ёмкости банка. */
#define UMP_POST_POINTS (UMP_LOG_CAPACITY / 10U)
/* Замораживает заполненный банк и переключает рабочий банк, сохраняя доступный архив. */
static void ump_freeze(void);
/* Latest PM67 sums: register 306 (index 305), register 9 (index 8). */
/* Объявляет ump_pm67_voltage — данные ump_pm67_voltage. */
static unsigned int ump_pm67_voltage[2];
/* Объявляет ump_prev_commands — данные ump_prev_commands; ump_prev_inputs — данные ump_prev_inputs; ump_prev_outputs — данные ump_prev_outputs. */
static unsigned int ump_prev_commands, ump_prev_inputs, ump_prev_outputs;
/* Called once at boot, before enabling interrupts. TI COFF startup does
* not clear uninitialized globals in .ebss. Do not use for record restart:
* restart must preserve the archive, time and latest received voltages. */
/* Явно инициализирует оба банка регистратора до включения IRQ; повторный старт записи архив не стирает. */
void ump_log_init(void)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет i — данные i. */
unsigned int i;
/* Записывает ump_state значением 0; ump_count = 0; ump_cursor = 0; ump_next = 0; данные ump_state. */
ump_state = 0; ump_count = 0; ump_cursor = 0; ump_next = 0;
/* Записывает ump_period значением 20; ump_divider = 0; период записи регистратора в миллисекундных тактах. */
ump_period = 20; ump_divider = 0;
/* Записывает ump_time значением 0; ump_sequence = 0; миллисекундное время регистратора. */
ump_time = 0; ump_sequence = 0;
/* Записывает ump_bank значением 0; ump_archive_bank = 1; индекс активного банка регистратора. */
ump_bank = 0; ump_archive_bank = 1;
/* Записывает ump_archive_count значением 0; ump_archive_next = 0; ump_generation = 0; ump archive число. */
ump_archive_count = 0; ump_archive_next = 0; ump_generation = 0;
/* Записывает ump_source значением 0; ump_post = 0; данные ump_source. */
ump_source = 0; ump_post = 0;
/* Цикл for (i = 0; i < 2; i++) ump_pm67_voltage[i] = 0;; задаёт начальное значение, условие продолжения и шаг перебора. */
for (i = 0; i < 2; i++) ump_pm67_voltage[i] = 0;
/* Записывает ump_prev_commands значением 0; ump_prev_inputs = 0; ump_prev_outputs = 0; данные ump_prev_commands. */
ump_prev_commands = 0; ump_prev_inputs = 0; ump_prev_outputs = 0;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Логические уровни входов платы нагрузки: физический ноль означает 1.
* Чтение GPIO непосредственно здесь позволяет записать вход до следующего
* обновления регистра Inputs в основном цикле. */
/* Читает активные входы START/STOP/разрешения непосредственно из GPIO с учётом полярности. */
static unsigned int ump_inputs(void)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Завершает ump_inputs и возвращает (!GpioDataRegs.GPADAT.bit.GPIO6) |. */
return (!GpioDataRegs.GPADAT.bit.GPIO6) |
/* Продолжает список аргументов, полей или выражение ((!GpioDataRegs.GPADAT.bit.GPIO8) << 1) |; порядок элементов сохраняется. */
((!GpioDataRegs.GPADAT.bit.GPIO8) << 1) |
/* Продолжает список аргументов, полей или выражение ((!GpioDataRegs.GPADAT.bit.GPIO11) << 2);; порядок элементов сохраняется. */
((!GpioDataRegs.GPADAT.bit.GPIO11) << 2);
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* START/STOP активны единицей; RES_OUT_2 активен физическим нулём. */
/* Собирает фактические состояния GPIO выходов START/STOP/разрешения нагрузки. */
static unsigned int ump_outputs(void)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Завершает ump_outputs и возвращает GpioDataRegs.GPBDAT.bit.GPIO62 |. */
return GpioDataRegs.GPBDAT.bit.GPIO62 |
/* Продолжает список аргументов, полей или выражение (GpioDataRegs.GPBDAT.bit.GPIO58 << 1) |; порядок элементов сохраняется. */
(GpioDataRegs.GPBDAT.bit.GPIO58 << 1) |
/* Продолжает список аргументов, полей или выражение ((!GpioDataRegs.GPADAT.bit.GPIO3) << 2);; порядок элементов сохраняется. */
((!GpioDataRegs.GPADAT.bit.GPIO3) << 2);
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Вызывается только внутри короткой критической секции. Все три источника
* записей (main, CAN ISR, timer ISR) используют одну блокировку IRQ с
* восстановлением прежнего состояния, включая вызовы из прерывания. */
/* Создаёт одну запись состояния: входы, выходы, команды, напряжения и параметры тока нагрузки. */
static void ump_capture(unsigned int reason)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет p — данные p. Хранит указатель на данные. volatile требует фактического чтения или записи при каждом доступе. */
volatile unsigned int *p;
/* Объявляет commands — данные commands; inputs — данные inputs; outputs — данные outputs. */
unsigned int commands, inputs, outputs;
/* Проверяет if (ump_state != 1 или Desk != dsk_LOAD или !READY) return; последующий оператор выполняется только при истинном условии. */
if (ump_state != 1 || Desk != dsk_LOAD || !READY) return;
/* Проверяет if (no_write или never_write); последующий оператор выполняется только при истинном условии. */
if (no_write || never_write) { ump_state = 3; return; }
/* Записывает commands значением Commands; данные commands. */
commands = Commands;
/* Записывает inputs значением ump_inputs(); outputs = ump_outputs(); данные inputs. */
inputs = ump_inputs(); outputs = ump_outputs();
/* Freeze only an actual running-to-stopped output transition.
* STOP is set before RES_OUT_2 is cleared in main; an interrupt can
* observe these writes separately, so require its level, not its edge.
* Repeated reset/STOP pulses while already stopped leave the archive. */
/* Проверяет if (!ump_post и (ump_prev_outputs & 4U) и !(outputs & 4U) и; последующий оператор выполняется только при истинном условии. */
if (!ump_post && (ump_prev_outputs & 4U) && !(outputs & 4U) &&
/* Продолжает список аргументов, полей или выражение (outputs & 2U)) {; порядок элементов сохраняется. */
(outputs & 2U)) {
/* Keep 90% including the trigger; reserve 10% for later records. */
/* Проверяет if (ump_count >= UMP_LOG_CAPACITY - UMP_POST_POINTS); последующий оператор выполняется только при истинном условии. */
if (ump_count >= UMP_LOG_CAPACITY - UMP_POST_POINTS)
/* Записывает ump_count значением UMP_LOG_CAPACITY - UMP_POST_POINTS - 1; число сохранённых точек в активном банке. */
ump_count = UMP_LOG_CAPACITY - UMP_POST_POINTS - 1;
/* Записывает ump_post значением UMP_POST_POINTS; данные ump_post. */
ump_post = UMP_POST_POINTS;
/* Устанавливает биты reason значением 0x100; данные reason. */
reason |= 0x100;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Записывает ump_prev_commands значением commands; ump_prev_inputs = inputs; ump_prev_outputs = outputs; данные ump_prev_commands. */
ump_prev_commands = commands; ump_prev_inputs = inputs; ump_prev_outputs = outputs;
/* Every stored point counts, including CAN and I/O, except the trigger. */
/* Проверяет if (ump_post и !(reason & 0x100) и --ump_post == 0) reason = 5; последующий оператор выполняется только при истинном условии. */
if (ump_post && !(reason & 0x100) && --ump_post == 0) reason = 5;
/* Записывает p значением logs_block + ((unsigned long)ump_bank * UMP_LOG_CAPACITY + ump_next) * UMP_LOG_WORDS; данные p. */
p = logs_block + ((unsigned long)ump_bank * UMP_LOG_CAPACITY + ump_next) * UMP_LOG_WORDS;
/* Сохраняет 32-битное миллисекундное время младшим словом вперёд; это первые два слова записи v4. */
p[0] = (unsigned int)ump_time; p[1] = (unsigned int)(ump_time >> 16);
/* Событие сохраняет причину и метку отключения; от команд остаются только нужные дискретные пуск и сброс, биты 7/6. */
p[2] = reason; p[3] = commands & 0xC0U;
/* Оставляет логические уровни входов и выходов для графиков START, STOP, OMEGA и разрешения нагрузки. */
p[4] = inputs; p[5] = outputs;
/* Задание тока берётся из регистра 0x1C в единицах 0,1 мА; это расчётная величина, а не измерение физического тока. */
p[6] = modbus[0x1C];
/* Сохраняет оставшиеся выбранные начальную и конечную уставки в целых мА. */
p[7] = DAC_go; p[8] = DAC_stop;
/* Оставляет оба ЗПТ в вольтах: CAN-индекс 305 относится к ЗПТ1, индекс 8 — к ЗПТ2. */
p[9] = ump_pm67_voltage[0]; p[10] = ump_pm67_voltage[1];
/* Резервное нулевое слово делает длину записи чётной: служебный CAN передаёт по два 16-битных слова за фрагмент. */
p[11] = 0;
/* Изменяет счётчик в выражении ump_next++; на один для следующего шага. */
ump_next++;
/* Проверяет if (ump_next == UMP_LOG_CAPACITY) ump_next = 0; последующий оператор выполняется только при истинном условии. */
if (ump_next == UMP_LOG_CAPACITY) ump_next = 0;
/* Проверяет if (ump_count < UMP_LOG_CAPACITY) ump_count++; последующий оператор выполняется только при истинном условии. */
if (ump_count < UMP_LOG_CAPACITY) ump_count++;
/* Изменяет счётчик в выражении ump_sequence++; на один для следующего шага. */
ump_sequence++;
/* Проверяет if (reason == 5) ump_freeze(); последующий оператор выполняется только при истинном условии. */
if (reason == 5) ump_freeze();
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Завершённый банк больше не изменяется до следующего отключения.
* Номер поколения позволяет GUI обнаружить замену архива во время чтения. */
/* Замораживает заполненный банк и переключает рабочий банк, сохраняя доступный архив. */
static void ump_freeze(void)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Записывает ump_archive_bank значением ump_bank; индекс замороженного банка, доступного для чтения. */
ump_archive_bank = ump_bank;
/* Записывает ump_archive_count значением ump_count; ump_archive_next = ump_next; ump archive число. */
ump_archive_count = ump_count; ump_archive_next = ump_next;
/* Изменяет счётчик в выражении ump_generation++; на один для следующего шага. */
ump_generation++;
/* Инвертирует биты ump_bank значением 1; индекс активного банка регистратора. */
ump_bank ^= 1;
/* Записывает ump_count значением 0; ump_next = 0; ump_post = 0; число сохранённых точек в активном банке. */
ump_count = 0; ump_next = 0; ump_post = 0;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Продвигает время регистратора в Timer1 и снимает записи с заданным периодом. */
void ump_log_tick(void)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет saved — данные saved. Начальное значение задаётся справа от знака равенства. */
unsigned int saved = __disable_interrupts();
/* Проверяет if (ump_state == 1); последующий оператор выполняется только при истинном условии. */
if (ump_state == 1) {
/* Изменяет счётчик в выражении ump_time++; на один для следующего шага. */
ump_time++;
/* Проверяет if (++ump_divider >= ump_period); последующий оператор выполняется только при истинном условии. */
if (++ump_divider >= ump_period) { ump_divider = 0; ump_capture(1); }
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Вызывает __restore_interrupts с аргументами (saved) в текущей последовательности операций. */
__restore_interrupts(saved);
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Дополнительный снимок только при изменении команд, входов или выходов.
* Это не заменяет периодическую запись аналоговых величин в таймере. */
/* Обслуживает изменения дискретных входов и выходов из главного цикла. */
void ump_log_io(void)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет saved — данные saved. */
unsigned int saved;
/* Проверяет if (ump_state != 1 или Desk != dsk_LOAD) return; последующий оператор выполняется только при истинном условии. */
if (ump_state != 1 || Desk != dsk_LOAD) return;
/* Записывает saved значением __disable_interrupts(); данные saved. */
saved = __disable_interrupts();
/* Проверяет if (Commands != ump_prev_commands или ump_inputs() != ump_prev_inputs или; последующий оператор выполняется только при истинном условии. */
if (Commands != ump_prev_commands || ump_inputs() != ump_prev_inputs ||
/* Вызывает ump_outputs. Собирает фактические состояния GPIO выходов START/STOP/разрешения нагрузки. */
ump_outputs() != ump_prev_outputs) ump_capture(3);
/* Вызывает __restore_interrupts с аргументами (saved) в текущей последовательности операций. */
__restore_interrupts(saved);
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Вызывается после применения слов принятого CAN-кадра: снимок содержит
* новые команды, а также исходный адрес/маску и все три слова кадра. */
/* Обновляет последние CAN-данные и напряжения для следующего снимка регистратора. */
void ump_log_can(unsigned int header, unsigned int a, unsigned int b, unsigned int c)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет saved — данные saved; i — данные i; address — адрес; mask — маска; data — данные. */
unsigned int saved, i, address, mask, data[3];
/* Проверяет if (Desk != dsk_LOAD) return; последующий оператор выполняется только при истинном условии. */
if (Desk != dsk_LOAD) return;
/* Записывает saved значением __disable_interrupts(); данные saved. */
saved = __disable_interrupts();
/* Записывает data[0] значением a; data[1] = b; data[2] = c; данные. */
data[0] = a; data[1] = b; data[2] = c;
/* Записывает address значением header & 0x1FFFU; адрес. */
address = header & 0x1FFFU;
/* Записывает mask значением 0x8000U; маска. */
mask = 0x8000U;
/* Track reception even while recording is stopped. Each mask bit
* refers to its own address, including disabled words in between. */
/* Цикл for (i = 0; i < 3; i++, address++, mask >>= 1) ; задаёт начальное значение, условие продолжения и шаг перебора. */
for (i = 0; i < 3; i++, address++, mask >>= 1) {
/* Проверяет if (!(header & mask)) continue; последующий оператор выполняется только при истинном условии. */
if (!(header & mask)) continue;
/* Проверяет if (address == 305U) ump_pm67_voltage[0] = data[i]; последующий оператор выполняется только при истинном условии. */
if (address == 305U) ump_pm67_voltage[0] = data[i];
/* Проверяет if (address == 8U) ump_pm67_voltage[1] = data[i]; последующий оператор выполняется только при истинном условии. */
if (address == 8U) ump_pm67_voltage[1] = data[i];
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Проверяет if (ump_state != 1); последующий оператор выполняется только при истинном условии. */
if (ump_state != 1) { __restore_interrupts(saved); return; }
/* Вызывает ump_capture. Создаёт одну запись состояния: входы, выходы, команды, напряжения и параметры тока нагрузки. */
ump_capture(2);
/* Вызывает __restore_interrupts с аргументами (saved) в текущей последовательности операций. */
__restore_interrupts(saved);
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Регистрирует изменение слова команд после записи по последовательному интерфейсу. */
void ump_log_rs(void)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет saved — данные saved. Начальное значение задаётся справа от знака равенства. */
unsigned int saved = __disable_interrupts();
/* Вызывает ump_capture. Создаёт одну запись состояния: входы, выходы, команды, напряжения и параметры тока нагрузки. */
ump_capture(4);
/* Вызывает __restore_interrupts с аргументами (saved) в текущей последовательности операций. */
__restore_interrupts(saved);
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Управление журналом отделено от технологических команд: начало записи
* НЕ запускает УМП. Выбор архива не останавливает текущую запись. */
/* Применяет допустимые команды управления регистратором, сохраняя ограничения адресов. */
int ump_log_write(unsigned int address, unsigned int value)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет saved — данные saved; valid — данные valid. Начальное значение задаётся справа от знака равенства. */
unsigned int saved, valid = 1;
/* Записывает saved значением __disable_interrupts(); данные saved. */
saved = __disable_interrupts();
/* Проверяет if (address == UMP_LOG_BASE и value == 0); последующий оператор выполняется только при истинном условии. */
if (address == UMP_LOG_BASE && value == 0) {
/* Записывает ump_state значением 3; ump_post = 0; данные ump_state. */
ump_state = 3; ump_post = 0;
/* Проверяет else if (address == UMP_LOG_BASE и value == 1 и Desk == dsk_LOAD и READY и ump_state != 1); последующий оператор выполняется только при истинном условии. */
} else if (address == UMP_LOG_BASE && value == 1 && Desk == dsk_LOAD && READY && ump_state != 1) {
/* Записывает ump_count значением 0; ump_next = 0; ump_cursor = 0; ump_divider = 0; ump_post = 0; число сохранённых точек в активном банке. */
ump_count = 0; ump_next = 0; ump_cursor = 0; ump_divider = 0; ump_post = 0;
/* Записывает ump_source значением 0; данные ump_source. */
ump_source = 0;
/* Записывает ump_prev_commands значением Commands; данные ump_prev_commands. */
ump_prev_commands = Commands;
/* Записывает ump_prev_inputs значением ump_inputs(); ump_prev_outputs = ump_outputs(); данные ump_prev_inputs. */
ump_prev_inputs = ump_inputs(); ump_prev_outputs = ump_outputs();
/* Записывает no_write значением 0; never_write = 0; no запись. */
no_write = 0; never_write = 0;
/* Записывает ump_state значением 1; данные ump_state. */
ump_state = 1;
/* Вызывает ump_capture. Создаёт одну запись состояния: входы, выходы, команды, напряжения и параметры тока нагрузки. */
ump_capture(1);
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Проверяет else if (address == UMP_LOG_BASE+1 и ump_state != 1 и value >= 1 и value <= 1000); последующий оператор выполняется только при истинном условии. */
else if (address == UMP_LOG_BASE+1 && ump_state != 1 && value >= 1 && value <= 1000)
/* Записывает ump_period значением value; период записи регистратора в миллисекундных тактах. */
ump_period = value;
/* Проверяет else if (address == UMP_LOG_BASE+2 и value < UMP_LOG_CAPACITY); последующий оператор выполняется только при истинном условии. */
else if (address == UMP_LOG_BASE+2 && value < UMP_LOG_CAPACITY)
/* Записывает ump_cursor значением value; данные ump_cursor. */
ump_cursor = value;
/* Проверяет else if (address == UMP_LOG_BASE+3 и value <= 1) ump_source = value; последующий оператор выполняется только при истинном условии. */
else if (address == UMP_LOG_BASE+3 && value <= 1) ump_source = value;
/* Выполняет альтернативу, если предыдущее условие if оказалось ложным. */
else valid = 0;
/* Вызывает __restore_interrupts с аргументами (saved) в текущей последовательности операций. */
__restore_interrupts(saved);
/* Завершает ump_log_write и возвращает valid. */
return valid;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Преобразование хронологического индекса в физическую позицию кольца.
* Для архива сохранены собственные count/next, независимые от живого банка. */
/* Начинает функцию ump_word; назначение модуля — двухбанковый регистратор событий нагрузки во внешней RAM. */
static unsigned int ump_word(unsigned int archived, unsigned int record, unsigned int word)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет count — число; ump_count — число сохранённых точек в активном банке. Начальное значение задаётся справа от знака равенства. */
unsigned int count = archived ? ump_archive_count : ump_count;
/* Объявляет next — данные next; ump_next — данные ump_next. Начальное значение задаётся справа от знака равенства. */
unsigned int next = archived ? ump_archive_next : ump_next;
/* Объявляет bank — данные bank; ump_bank — индекс активного банка регистратора. Начальное значение задаётся справа от знака равенства. */
unsigned int bank = archived ? ump_archive_bank : ump_bank;
/* Объявляет slot — данные slot. */
unsigned int slot;
/* Проверяет if (record >= count) return 0; последующий оператор выполняется только при истинном условии. */
if (record >= count) return 0;
/* Записывает slot значением next + UMP_LOG_CAPACITY - count + record; данные slot. */
slot = next + UMP_LOG_CAPACITY - count + record;
/* Повторяет или ожидает, пока выполняется while (slot >= UMP_LOG_CAPACITY) slot -= UMP_LOG_CAPACITY;. */
while (slot >= UMP_LOG_CAPACITY) slot -= UMP_LOG_CAPACITY;
/* Завершает ump_word и возвращает logs_block[((unsigned long)bank * UMP_LOG_CAPACITY + slot) * UMP_LOG_WORDS + word]. */
return logs_block[((unsigned long)bank * UMP_LOG_CAPACITY + slot) * UMP_LOG_WORDS + word];
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Весь Modbus-блок читается вызывающим кодом под одной блокировкой IRQ.
* Поэтому timestamp/данные и заголовок онлайн-блока относятся к одному
* состоянию кольца, даже если в этот момент приходит новая CAN-команда. */
/* Проверяет область чтения регистратора и возвращает слова состояния либо архивной записи. */
unsigned int ump_log_read(unsigned int address)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет offset — данные offset; count — число. */
unsigned int offset, count;
/* Объявляет first — данные first. */
unsigned long first;
/* Вызывает switch с аргументами (address) { в текущей последовательности операций. */
switch (address) {
/* Ветвь выбора для UMP_LOG_BASE; выполняется при совпадении селектора switch. */
case UMP_LOG_BASE: return 0x554D;
/* Ветвь выбора для UMP_LOG_BASE+1; выполняется при совпадении селектора switch. */
case UMP_LOG_BASE+1: return 4;
/* Ветвь выбора для UMP_LOG_BASE+2; выполняется при совпадении селектора switch. */
case UMP_LOG_BASE+2: return ump_source ? 3 : ump_state;
/* Ветвь выбора для UMP_LOG_BASE+3; выполняется при совпадении селектора switch. */
case UMP_LOG_BASE+3: return ump_source ? ump_archive_count : ump_count;
/* Ветвь выбора для UMP_LOG_BASE+4; выполняется при совпадении селектора switch. */
case UMP_LOG_BASE+4: return UMP_LOG_WORDS;
/* Ветвь выбора для UMP_LOG_BASE+5; выполняется при совпадении селектора switch. */
case UMP_LOG_BASE+5: return UMP_LOG_CAPACITY;
/* Ветвь выбора для UMP_LOG_BASE+6; выполняется при совпадении селектора switch. */
case UMP_LOG_BASE+6: return ump_period;
/* Ветвь выбора для UMP_LOG_BASE+7; выполняется при совпадении селектора switch. */
case UMP_LOG_BASE+7: return (unsigned int)ump_time;
/* Ветвь выбора для UMP_LOG_BASE+8; выполняется при совпадении селектора switch. */
case UMP_LOG_BASE+8: return (unsigned int)(ump_time >> 16);
/* Ветвь выбора для UMP_LOG_BASE+9; выполняется при совпадении селектора switch. */
case UMP_LOG_BASE+9: return Desk;
/* Ветвь выбора для UMP_LOG_BASE+10; выполняется при совпадении селектора switch. */
case UMP_LOG_BASE+10: return Mode;
/* Ветвь выбора для UMP_LOG_BASE+11; выполняется при совпадении селектора switch. */
case UMP_LOG_BASE+11: return (unsigned int)READY_FREQ;
/* Ветвь выбора для UMP_LOG_BASE+12; выполняется при совпадении селектора switch. */
case UMP_LOG_BASE+12: return ump_generation;
/* Ветвь выбора для UMP_LOG_BASE+13; выполняется при совпадении селектора switch. */
case UMP_LOG_BASE+13: return ump_archive_count;
/* Preserve the legacy millisecond status field as an upper estimate.
* Additional event records may finish the post interval sooner. */
/* Ветвь выбора для UMP_LOG_BASE+14; выполняется при совпадении селектора switch. */
case UMP_LOG_BASE+14:
/* Записывает first значением (unsigned long)ump_post * ump_period; данные first. */
first = (unsigned long)ump_post * ump_period;
/* Завершает ump_log_read и возвращает first > 65535UL ? 65535U : (unsigned int)first. */
return first > 65535UL ? 65535U : (unsigned int)first;
/* Ветвь выбора для UMP_LOG_BASE+15; выполняется при совпадении селектора switch. */
case UMP_LOG_BASE+15: return ump_state;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Проверяет if (address >= UMP_LOG_DATA и address < UMP_LOG_DATA+120 и (ump_source или ump_state != 1)); последующий оператор выполняется только при истинном условии. */
if (address >= UMP_LOG_DATA && address < UMP_LOG_DATA+120 && (ump_source || ump_state != 1)) {
/* Записывает offset значением address - UMP_LOG_DATA; данные offset. */
offset = address - UMP_LOG_DATA;
/* Завершает ump_log_read и возвращает ump_word(ump_source, ump_cursor + offset / UMP_LOG_WORDS, offset % UMP_LOG_WORDS). */
return ump_word(ump_source, ump_cursor + offset / UMP_LOG_WORDS, offset % UMP_LOG_WORDS);
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Последние UMP_LOG_LIVE_RECORDS записей для онлайн-графика, без остановки и без
* изменения курсора архивного чтения. Первые два слова — их номер. */
/* Проверяет if (address >= UMP_LOG_LIVE и address < UMP_LOG_LIVE+124); последующий оператор выполняется только при истинном условии. */
if (address >= UMP_LOG_LIVE && address < UMP_LOG_LIVE+124) {
/* Записывает count значением ump_count < UMP_LOG_LIVE_RECORDS ? ump_count : UMP_LOG_LIVE_RECORDS; число. */
count = ump_count < UMP_LOG_LIVE_RECORDS ? ump_count : UMP_LOG_LIVE_RECORDS;
/* Записывает first значением ump_sequence - count; данные first. */
first = ump_sequence - count;
/* Записывает offset значением address - UMP_LOG_LIVE; данные offset. */
offset = address - UMP_LOG_LIVE;
/* Проверяет if (offset == 0) return (unsigned int)first; последующий оператор выполняется только при истинном условии. */
if (offset == 0) return (unsigned int)first;
/* Проверяет if (offset == 1) return (unsigned int)(first >> 16); последующий оператор выполняется только при истинном условии. */
if (offset == 1) return (unsigned int)(first >> 16);
/* Проверяет if (offset == 2) return count; последующий оператор выполняется только при истинном условии. */
if (offset == 2) return count;
/* Проверяет if (offset == 3) return ump_generation; последующий оператор выполняется только при истинном условии. */
if (offset == 3) return ump_generation;
/* Уменьшает offset значением 4; данные offset. */
offset -= 4;
/* Проверяет if (offset / UMP_LOG_WORDS < count); последующий оператор выполняется только при истинном условии. */
if (offset / UMP_LOG_WORDS < count)
/* Завершает ump_log_read и возвращает ump_word(0, ump_count - count + offset / UMP_LOG_WORDS, offset % UMP_LOG_WORDS). */
return ump_word(0, ump_count - count + offset / UMP_LOG_WORDS, offset % UMP_LOG_WORDS);
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Завершает ump_log_read и возвращает 0. */
return 0;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}