Files
Andrey 05be38a04a Обновить Flash/CAN, версии сборок и управление периферией
Добавить защищённое обновление приложения через RS и CAN с сохранением
загрузчика, проверкой CRC, восстановлением передачи и защитой EEPROM.
Подключить общий процесс сборки C2000 и автоматический счётчик версии
для согласованных RAM, Flash, CAN и паспортов бинарных образов.

Уточнить запуск ЦАП, обработку измерений и параметров нагрузки,
начальную индикацию и документацию регистров GAS.
Дополнить модели аппаратуры и изолировать заголовки CAN в тестах.

Проверка: 26 тестов выпуска, 4 теста восстановления CAN и восемь
программных моделей CAN, Flash, EEPROM, ЦАП, параметров и TESTLAMP.
2026-11-06 16:11:11 +03:00

704 lines
58 KiB
C
Raw Permalink 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.
/* Обработка Modbus, заводские параметры и сохранение настроек. Русские пояснения сохранены в UTF-8. */
/* Подключает "eeprom_config.h": выбор профиля физически установленной EEPROM при сборке. */
#include "eeprom_config.h"
/* Подключает "include/balsam167_eeprom_block.h": настройки и интерфейс balsam167_eeprom_block. */
#include "include/balsam167_eeprom_block.h"
/* Подключает "DSP2833x_Device.h": поставляемое TI описание Device для семейства C2833x. */
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
/* Подключает "package.h": адреса устройств, типы плат и псевдонимы технологических регистров. */
#include "package.h"
/* Подключает "RS485.h": приём и передача SCI-A/SCI-B, упаковка команд и FIFO первого порта. */
#include "RS485.h"
/* Подключает "crc16.h": таблицы и функции вычисления контрольных сумм CRC. */
#include "crc16.h"
/* Подключает "cntrl_adr.h": собственный и универсальный адрес устройства. */
#include "cntrl_adr.h"
/* Подключает "bios_dsp.h": коды и объявления команд последовательного загрузчика. */
#include "bios_dsp.h"
/* Подключает "message.h": обработка Modbus, заводские параметры и сохранение настроек. */
#include "message.h"
/* Подключает "filter_bat2.h": накопительный фильтр измерительных каналов. */
#include "filter_bat2.h"
/* Подключает "measure.h": обработка датчиков, вычисление величин и миллисекундный такт системы. */
#include "measure.h"
/* Подключает "ADC.h": настройка АЦП, обработка преобразований и первичная фильтрация измерений. */
#include "ADC.h"
/* Подключает "peripher.h": перемычки режима платы, дискретные входы и выходы. */
#include "peripher.h"
/* Подключает "ecan.h": штатный eCAN и отдельный служебный обмен УМП/GAS. */
#include "ecan.h"
/* Подключает "pulto.h": обмен и индикация пульта. */
#include "pulto.h"
/* Подключает "spise2p.h": драйвер SPI EEPROM, адресация, страницы и автомат завершения операций. */
#include "spise2p.h"
/* Подключает "caliber.h": заводские калибровочные значения датчиков. */
#include "caliber.h"
/* Подключает "log_to_mem.h": двухбанковый регистратор событий нагрузки во внешней RAM. */
#include "log_to_mem.h"
/* Подключает "DAC.h": выход ЦАП и автомат защиты нагрузки с проверкой напряжения и свежести CAN. */
#include "DAC.h"
/* Подключает "ump_tms28335.h": настройки и интерфейс ump_tms28335. */
#include "ump_tms28335.h"
/* Подключает "gas167.h": дополнительная карта GAS 167/v1 только для новой EEPROM M95M02. */
#include "gas167.h"
/* Подключает "include/balsam167_eeprom_layout.h": настройки и интерфейс balsam167_eeprom_layout. */
#include "include/balsam167_eeprom_layout.h"
/* Условие компиляции (ANSWER_LEN + 1) != BALSAM167_EEPROM_PARAM_WORDS: выбранный блок включается в текущий вариант прошивки. */
#if (ANSWER_LEN + 1) != BALSAM167_EEPROM_PARAM_WORDS
/* Останавливает сборку при недопустимой конфигурации: EEPROM_parameter_reservation_must_match_ANSWER_LEN. */
#error EEPROM_parameter_reservation_must_match_ANSWER_LEN
/* Закрывает условный блок препроцессора; дальнейшие строки относятся к внешнему уровню. */
#endif
/* Объявляет modbus — массив технологических регистров; слово int на C28x занимает 16 бит. */
int modbus[ANSWER_LEN+1];
/* Объявляет param — данные param. */
unsigned int param[ANSWER_LEN+1];
/* Сброс параметров не является командой пуска: у работающей нагрузки сохраняются только текущие START/RESET, полученные по интерфейсу. */
static void reset_parameter_commands(void) /* Объединяет поведение Load/Default и исключает пуск из сохранённого слова R127. */
{ /* Начинает короткую критическую секцию без EEPROM, ожиданий и переключения GPIO. */
unsigned int saved = __disable_interrupts(); /* Сохраняет маску IRQ, чтобы команда CAN не затерялась между чтением и очисткой служебных битов. */
if (Desk == dsk_LOAD && READY) Commands &= 0x00C0U; /* Биты 7/6 — START/RESET; сохранение действующих уровней не сбрасывает RUN, STOP и запрет повтора автомата167. */
else Commands = 0; /* До готовности платы и у остальных типов очищает все команды: EEPROM и неинициализированная RAM не задают пуск. */
__restore_interrupts(saved); /* Возвращает прежнюю маску, включая вызов внутри защищённого применения параметров. */
} /* Завершает сброс служебных команд, не меняя счётчики, импульсы и состояние защиты нагрузки. */
/* Заполняет регистры заводскими значениями; для LOAD задаёт тайм-аут свежести CAN 2000 мс. Значение 600 мс применяется отдельно при восстановлении недопустимого параметра. */
void Default_params()
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет индекс параметров, номер заводской калибровки и сохранённую маску IRQ. */
unsigned int i,def,saved; /* Хранит индекс, номер заводской калибровки и прежнюю маску IRQ для согласованного применения параметров. */
/* Объявляет shkf_lo_edge — данные shkf_lo_edge. Начальное значение задаётся справа от знака равенства. */
int shkf_lo_edge[] = {300, // 380
/* Элемент постоянной таблицы 300,; порядок значений совпадает с порядком индексов потребителя. */
300, // 38O
/* Элемент постоянной таблицы 0,; порядок значений совпадает с порядком индексов потребителя. */
0, // 0
/* Элемент постоянной таблицы 0,; порядок значений совпадает с порядком индексов потребителя. */
0, // 0
/* Элемент постоянной таблицы 15,; порядок значений совпадает с порядком индексов потребителя. */
15, // 31
/* Элемент постоянной таблицы 22,; порядок значений совпадает с порядком индексов потребителя. */
22, // 27
/* Элемент постоянной таблицы 20,; порядок значений совпадает с порядком индексов потребителя. */
20, // 24
/* Элемент постоянной таблицы 10,; порядок значений совпадает с порядком индексов потребителя. */
10, // 15
/* Элемент постоянной таблицы 60};; порядок значений совпадает с порядком индексов потребителя. */
60}; // AD
/* Объявляет bright — данные bright. Начальное значение задаётся справа от знака равенства. */
int bright[] = {10,8,6,8,6,8};
saved = __disable_interrupts(); /* Не позволяет Timer1 увидеть промежуточные нулевые лимит и тайм-аут защиты при заполнении заводских параметров. */
/* Цикл for(i=0;i<ANSWER_LEN;i++); задаёт начальное значение, условие продолжения и шаг перебора. */
for(i=0;i<ANSWER_LEN;i++)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Записывает Modbus[i].all значением 0; представление регистра целым словом без разбиения на битовые поля. */
if (Desk != dsk_LOAD || i != 127U) Modbus[i].all = 0; /* Слово R127 нагрузки содержит живые команды; его очистка создала бы ложный STOP и повторный START. */
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Записывает LastMode значением Mode; последний режим платы, технологический регистр 126. */
LastMode = Mode;
/* Проверяет if(Desk == dsk_EPLT); последующий оператор выполняется только при истинном условии. */
if(Desk == dsk_EPLT)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Записывает Cancount[m_FAST] значением 2; данные Cancount. */
Cancount[m_FAST] = 2; // пауза между посылками CAN
/* Цикл for(i=0;i<6;i++) Bright[i] = bright[i];; задаёт начальное значение, условие продолжения и шаг перебора. */
for(i=0;i<6;i++) Bright[i] = bright[i];
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Выполняет альтернативу, если предыдущее условие if оказалось ложным. */
else
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Записывает Cancount[m_FAST] значением 10; данные Cancount. */
Cancount[m_FAST] = 10; // пауза между посылками CAN
/* Записывает Brightness значением 8; данные Brightness. */
Brightness = 8;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Записывает Cancount[m_SLOW] значением 101; данные Cancount. */
Cancount[m_SLOW] = 101; // пауза между посылками CAN
/* Проверяет if(Owen) Owncount = 20; последующий оператор выполняется только при истинном условии. */
if(Owen) Owncount = 20; // пауза между опросами OWEN
/* Проверяет if(Kalibro); последующий оператор выполняется только при истинном условии. */
if(Kalibro)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Включает следующий блок только при определённом символе CALIB_RS. */
#ifdef CALIB_RS
/* Записывает def значением Mode-1; данные def. */
def = Mode-1;
/* Проверяет if(Mode>adr_PULT) def--; последующий оператор выполняется только при истинном условии. */
if(Mode>adr_PULT) def--;
/* Выбирает альтернативный блок текущего условия компиляции. */
#else
/* Записывает def значением Mode-3; данные def. */
def = Mode-3;
/* Проверяет if(Mode==adr_SHKF) def=4; последующий оператор выполняется только при истинном условии. */
if(Mode==adr_SHKF) def=4;
/* Закрывает условный блок препроцессора; дальнейшие строки относятся к внешнему уровню. */
#endif
/* Цикл for(i=0;i<8;i++) Caliber[i] = def_cal[def][i];; задаёт начальное значение, условие продолжения и шаг перебора. */
for(i=0;i<8;i++) Caliber[i] = def_cal[def][i];
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Проверяет if(Desk == dsk_COMM); последующий оператор выполняется только при истинном условии. */
if(Desk == dsk_COMM)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Цикл for(i=0;i<TPL_POW*2+Owen;i++); задаёт начальное значение, условие продолжения и шаг перебора. */
for(i=0;i<TPL_POW*2+Owen;i++)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Записывает sens_hi_edge[i] значением 70; данные sens_hi_edge. */
sens_hi_edge[i] = 70;
/* Записывает sens_lo_edge[i] значением 60; данные sens_lo_edge. */
sens_lo_edge[i] = 60;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
} }
/* Проверяет if(Desk==dsk_BKSD); последующий оператор выполняется только при истинном условии. */
if(Desk==dsk_BKSD)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Цикл for(i=0;i<6;i++); задаёт начальное значение, условие продолжения и шаг перебора. */
for(i=0;i<6;i++)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Записывает sens_hi_edge[i] значением 170; данные sens_hi_edge. */
sens_hi_edge[i] = 170;
/* Записывает sens_lo_edge[i] значением 150; данные sens_lo_edge. */
sens_lo_edge[i] = 150;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Цикл for(i=6;i<8;i++); задаёт начальное значение, условие продолжения и шаг перебора. */
for(i=6;i<8;i++)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Записывает sens_hi_edge[i] значением 115; данные sens_hi_edge. */
sens_hi_edge[i] = 115;
/* Записывает sens_lo_edge[i] значением 105; данные sens_lo_edge. */
sens_lo_edge[i] = 105;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
} }
/* Проверяет if(Desk==dsk_BKST); последующий оператор выполняется только при истинном условии. */
if(Desk==dsk_BKST)
/* Цикл for(i=0;i<TPL_TRN*2;i++); задаёт начальное значение, условие продолжения и шаг перебора. */
for(i=0;i<TPL_TRN*2;i++)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Записывает sens_hi_edge[i] значением 70; данные sens_hi_edge. */
sens_hi_edge[i] = 70;
/* Записывает sens_lo_edge[i] значением 60; данные sens_lo_edge. */
sens_lo_edge[i] = 60;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Проверяет if(Mode==adr_SHKF); последующий оператор выполняется только при истинном условии. */
if(Mode==adr_SHKF)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Цикл for(i=0;i<15;i++) sens_lo_edge[i] = shkf_lo_edge[sens_type[i]];; задаёт начальное значение, условие продолжения и шаг перебора. */
for(i=0;i<15;i++) sens_lo_edge[i] = shkf_lo_edge[sens_type[i]];
/* Записывает sens_hi_edge[13] значением 70; данные sens_hi_edge. */
sens_hi_edge[13] = 70;
/* Записывает sens_hi_edge[14] значением 70; данные sens_hi_edge. */
sens_hi_edge[14] = 70;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Проверяет if(Desk==dsk_LOAD); последующий оператор выполняется только при истинном условии. */
if(Desk==dsk_LOAD)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Записывает sens_hi_edge[0] значением 420; данные sens_hi_edge. */
sens_hi_edge[0] = 420;
/* Записывает sens_hi_edge[1] значением 420; данные sens_hi_edge. */
sens_hi_edge[1] = 420;
/* Записывает sens_hi_edge[2] значением 900; данные sens_hi_edge. */
sens_hi_edge[2] = 900;
/* Записывает sens_hi_edge[3] значением 900; данные sens_hi_edge. */
sens_hi_edge[3] = 900;
/* Записывает sens_lo_edge[0] значением 300; данные sens_lo_edge. */
sens_lo_edge[0] = 300;
/* Записывает sens_lo_edge[1] значением 300; данные sens_lo_edge. */
sens_lo_edge[1] = 300;
/* Записывает sens_lo_edge[2] значением 800; данные sens_lo_edge. */
sens_lo_edge[2] = 800;
/* Записывает sens_lo_edge[3] значением 800; данные sens_lo_edge. */
sens_lo_edge[3] = 800;
/* Записывает DAC_20 значением Caliber[6]; ЦАП 20. */
DAC_20 = Caliber[6];
/* Записывает DAC_04 значением Caliber[7]; ЦАП 04. */
DAC_04 = Caliber[7];
// Пришло врема!
/* Записывает DAC_go значением 6; начальный ток нагрузки в мА, технологический регистр 0x50. */
DAC_go = 6; // начало зарада
LOAD_CAN_TIMEOUT = 2000; /* ms without fresh ZPT: freeze ramp. */ /* Записывает LOAD_CAN_TIMEOUT значением 2000; сохраняемый тайм-аут свежести напряжения CAN в миллисекундах, регистр 0x52. */
/* Устанавливает прежний заводской лимит пяти пусков, включая первый START; настройка хранится в сохраняемом регистре 0x7D. */
LOAD_MAX_ATTEMPTS = LOAD_DEFAULT_MAX_ATTEMPTS;
/* Записывает DAC_stop значением 16; конечный ток нагрузки в мА, технологический регистр 0x51. */
DAC_stop= 16; // конец зарада
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Проверяет if(Kurrent); последующий оператор выполняется только при истинном условии. */
if(Kurrent)
/* Цикл for(i=0;i<4;i++); задаёт начальное значение, условие продолжения и шаг перебора. */
for(i=0;i<4;i++)
/* Записывает Zero_lev[i] значением 0x800; данные Zero_lev. */
Zero_lev[i] = 0x800;
reset_parameter_commands(); /* Оставляет у готовой нагрузки текущие START/RESET и удаляет запросы Load/Default без повторения управляющих фронтов. */
__restore_interrupts(saved); /* После согласованной записи всех настроек снова разрешает Timer1 и CAN с прежней маской IRQ. */
/* Инициализирует модуль вызовом Init_sensors_more(); перед его дальнейшим использованием. */
Init_sensors_more();
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Читает сохранённые регистры из области параметров EEPROM и применяет настройки платы. */
void Load_params()
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет индекс параметров, контрольную сумму и сохранённую маску IRQ. */
unsigned int i,crc,saved; /* Хранит индекс, проверенную CRC и маску IRQ только для короткого копирования параметров из RAM. */
/* Вызывает Seeprom_read. Читает EEPROM порциями; адрес задаётся словами C28x, размер переводится в число передаваемых слов. */
Seeprom_read(BALSAM167_EEPROM_PARAMS_START_WORD, param, (ANSWER_LEN+1)*2);
/* Записывает crc значением get_crc16(param,ANSWER_LEN); данные crc. */
crc = get_crc16(param,ANSWER_LEN);
/* Проверяет if( (crc==param[ANSWER_LEN]) и; последующий оператор выполняется только при истинном условии. */
if( (crc==param[ANSWER_LEN]) &&
/* Продолжает список аргументов, полей или выражение (crc !=0xFFFF) &&; порядок элементов сохраняется. */
(crc !=0xFFFF) &&
/* Продолжает список аргументов, полей или выражение (Mode == param[126]) ); порядок элементов сохраняется. */
(Mode == param[126]) )
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Чтение EEPROM уже завершено: короткая секция ниже согласованно применяет настройки защиты из буфера param. */
saved = __disable_interrupts(); /* Применяет лимит попыток и тайм-аут единым снимком; длительное чтение EEPROM выше остаётся доступным для IRQ. */
for(i=0;i<ANSWER_LEN;i++) /* Переносит проверенную карту параметров, не выдавая аппаратных сигналов и не сбрасывая автомат167. */
if ((Desk != dsk_LOAD || i != 127U) && (Desk != dsk_EPLT || i >= 4U)) modbus[i] = param[i]; /* R127 нагрузки содержит живые START/RESET; R0..R3 пульта — два числа и две маски индикации, которые должны поступать по связи, а не восстанавливаться из старого EEPROM-снимка. */
reset_parameter_commands(); /* Сохраняет последние принятые START/RESET после чтения EEPROM; до READY очищает команды полностью. */
__restore_interrupts(saved); /* Возвращает исходное состояние IRQ после согласованного применения RAM-настроек. */
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Выполняет альтернативу, если предыдущее условие if оказалось ложным. */
else
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Восстанавливает заводские параметры после неверного CRC или несоответствия режима платы; LOAD_CAN_TIMEOUT получает 2000 мс. */
Default_params();
/* Default_params уже сохранила текущие управляющие уровни нагрузки; повторная очистка R127 здесь недопустима. */
/* Вызывает Save_params. Сохраняет технологические регистры в область параметров, отделённую от загрузочного образа EEPROM. */
Save_params();
/* Проверяет if(TermoSW); последующий оператор выполняется только при истинном условии. */
if(TermoSW)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Записывает cTermoCal значением 1; данные cTermoCal. */
cTermoCal =1;
/* Записывает Caliber_time значением 0xFFFF; данные Caliber_time. */
Caliber_time = 0xFFFF;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
} } }
/* Сохраняет технологические регистры в область параметров, отделённую от загрузочного образа EEPROM. */
void Save_params()
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет i — данные i; dif — данные dif. Начальное значение задаётся справа от знака равенства. */
unsigned int i,dif=0;
/* Цикл for(i=0;i<ANSWER_LEN;i++); задаёт начальное значение, условие продолжения и шаг перебора. */
for(i=0;i<ANSWER_LEN;i++)
/* Проверяет if(param[i] != modbus[i]); последующий оператор выполняется только при истинном условии. */
if(param[i] != modbus[i])
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Записывает param[i] значением modbus[i]; данные param. */
param[i] = modbus[i];
/* Записывает dif значением 1; данные dif. */
dif=1;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Проверяет if(dif); последующий оператор выполняется только при истинном условии. */
if(dif)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Записывает param[ANSWER_LEN] значением get_crc16(param,ANSWER_LEN); данные param. */
param[ANSWER_LEN] = get_crc16(param,ANSWER_LEN);
/* Вызывает Seeprom_write. Пишет EEPROM с разделением по физическим страницам; область параметров не пересекает образ прошивки. */
Seeprom_write(BALSAM167_EEPROM_PARAMS_START_WORD,param,(ANSWER_LEN+1)*2);
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
} }
/***************************************************************/
/***************************************************************/
/* Передача данных по протоколу ModBus - команда 3
Чтение ачеек данных */
/***************************************************************/
/***************************************************************/
/* Формирует ответ Modbus с установленным старшим битом функции и кодом исключения. */
static void modbus_exception(RS_DATA *rs_arr, unsigned int function, unsigned int error)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет crc — данные crc. */
unsigned int crc;
/* Записывает rs_arr->buffer[0] значением CNTRL_ADDR; rs arr буфер. */
rs_arr->buffer[0] = CNTRL_ADDR;
/* Записывает rs_arr->buffer[1] значением function | 0x80; rs arr буфер. */
rs_arr->buffer[1] = function | 0x80;
/* Записывает rs_arr->buffer[2] значением error; rs arr буфер. */
rs_arr->buffer[2] = error;
/* Записывает crc значением get_crc_16(0xFFFF, rs_arr->buffer, 3); данные crc. */
crc = get_crc_16(0xFFFF, rs_arr->buffer, 3);
/* Записывает rs_arr->buffer[3] значением LOBYTE(crc); rs_arr->buffer[4] = HIBYTE(crc); rs arr буфер. */
rs_arr->buffer[3] = LOBYTE(crc); rs_arr->buffer[4] = HIBYTE(crc);
/* Записывает rs_arr->flag_TIMEOUT_to_Send значением true; rs arr флаг TIMEOUT to Send. */
rs_arr->flag_TIMEOUT_to_Send = true;
/* Вызывает RS_Send. Передаёт массив слов через SCI в выбранном режиме упаковки. */
RS_Send(rs_arr, rs_arr->buffer, 5);
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* UART parser has already validated CRC and accepted the device address. */
/* Маршрутизирует расширенные адреса: RAM УМП/GAS захватывается под краткой блокировкой IRQ, а чтение EEPROM выполняется с разрешёнными IRQ, поскольку SPI продвигается из Timer2. */
static int receive_ump(RS_DATA *rs_arr)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет address — адрес; value — данные value; saved — данные saved; length — длина; count — число. */
unsigned int address, value, saved, length, count, error;
/* Main-loop only; encoded into the UART buffer before the next request.
* Keep the 124-word snapshot off the shared interrupt stack. */
/* Статический буфер хранит до 124 слов снимка вне общего стека прерываний; главный цикл сериализует его до обработки следующего запроса. */
static uint16_t words[UMP_MAX_WORDS];
/* Записывает address значением (rs_arr->RS_Header[2] << 8) | rs_arr->RS_Header[3]; адрес. */
address = (rs_arr->RS_Header[2] << 8) | rs_arr->RS_Header[3];
/* Проверяет if (address < UMP_BASE) return 0; последующий оператор выполняется только при истинном условии. */
if (address < UMP_BASE) return 0;
/* Записывает value значением (rs_arr->RS_Header[4] << 8) | rs_arr->RS_Header[5]; данные value. */
value = (rs_arr->RS_Header[4] << 8) | rs_arr->RS_Header[5];
/* SPI driver advances in Timer2 ISR: NEVER mask interrupts here.
* Reuse the existing snapshot buffer; the main loop owns this transaction.
* The shared validator rejects writes, wraparound and out-of-device reads. */
/* Проверяет if (address >= 0x1300U и address < 0x1308U); последующий оператор выполняется только при истинном условии. */
if (address >= 0x1300U && address < 0x1308U) {
/* Записывает count значением Load_protection_prepare(rs_arr->RS_Header[1], address, value, words, &error); число. */
count = Load_protection_prepare(rs_arr->RS_Header[1], address, value, words, &error);
/* Проверяет else if (address >= BALSAM167_EEPROM_RTU_BASE); последующий оператор выполняется только при истинном условии. */
} else if (address >= BALSAM167_EEPROM_RTU_BASE) {
/* Записывает error значением 2; ошибка. */
error = 2;
/* Условие компиляции EEPROM_M95M02: выбранный блок включается в текущий вариант прошивки. */
#if EEPROM_M95M02
/* Exception 3 prevents old AT-profile clients from falling back to
* legacy PEEK after a profile mismatch. No SPI transaction is issued. */
/* Проверяет if (!(value & BALSAM167_EEPROM_M95_MARKER)) error = 3; последующий оператор выполняется только при истинном условии. */
if (!(value & BALSAM167_EEPROM_M95_MARKER)) error = 3;
/* Закрывает условный блок препроцессора; дальнейшие строки относятся к внешнему уровню. */
#endif
/* Записывает count значением 0; число. */
count = 0;
/* Проверяет if (balsam167_eeprom_block_valid(rs_arr->RS_Header[1], address, value)); последующий оператор выполняется только при истинном условии. */
if (balsam167_eeprom_block_valid(rs_arr->RS_Header[1], address, value)) {
/* Условие компиляции EEPROM_M95M02: выбранный блок включается в текущий вариант прошивки. */
#if EEPROM_M95M02
/* Записывает count значением value & 255U; число. */
count = value & 255U;
/* Вызывает Seeprom_read. Читает EEPROM порциями; адрес задаётся словами C28x, размер переводится в число передаваемых слов. */
Seeprom_read(balsam167_eeprom_block_word(address, value), words, count * 2);
/* Выбирает альтернативный блок текущего условия компиляции. */
#else
/* Записывает count значением value; число. */
count = value;
/* Вызывает Seeprom_read. Читает EEPROM порциями; адрес задаётся словами C28x, размер переводится в число передаваемых слов. */
Seeprom_read(address - BALSAM167_EEPROM_RTU_BASE, words, count * 2);
/* Закрывает условный блок препроцессора; дальнейшие строки относятся к внешнему уровню. */
#endif
/* Записывает error значением 0; ошибка. */
error = 0;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Выполняет альтернативу, если предыдущее условие if оказалось ложным. */
} else {
/* Записывает saved значением __disable_interrupts(); данные saved. */
saved = __disable_interrupts();
/* Условие компиляции EEPROM_M95M02: выбранный блок включается в текущий вариант прошивки. */
#if EEPROM_M95M02 || defined(FLASH_BOOT) /* Карта GAS доступна физической M95M02 и отдельному исполняемому Flash-образу. */
/* Проверяет if (gas167_contains(address)); последующий оператор выполняется только при истинном условии. */
if (gas167_contains(address))
/* Записывает count значением gas167_prepare(rs_arr->RS_Header[1], address, value, 0, words, &error); число. */
count = gas167_prepare(rs_arr->RS_Header[1], address, value, 0, words, &error);
/* Выполняет альтернативу, если предыдущее условие if оказалось ложным. */
else
/* Закрывает условный блок препроцессора; дальнейшие строки относятся к внешнему уровню. */
#endif
/* Записывает count значением ump_tms28335_capture(rs_arr->RS_Header[1], address, value, words, &error); число. */
count = ump_tms28335_capture(rs_arr->RS_Header[1], address, value, words, &error);
/* Вызывает __restore_interrupts с аргументами (saved) в текущей последовательности операций. */
__restore_interrupts(saved);
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Записывает length значением ump_rtu_encode(CNTRL_ADDR, rs_arr->RS_Header[1], error,; длина. */
length = ump_rtu_encode(CNTRL_ADDR, rs_arr->RS_Header[1], error,
/* Продолжает список аргументов, полей или выражение words, count, rs_arr->buffer, MAX_SEND_LENGTH);; порядок элементов сохраняется. */
words, count, rs_arr->buffer, MAX_SEND_LENGTH);
/* Проверяет if (length); последующий оператор выполняется только при истинном условии. */
if (length) {
/* Записывает rs_arr->flag_TIMEOUT_to_Send значением true; rs arr флаг TIMEOUT to Send. */
rs_arr->flag_TIMEOUT_to_Send = true;
/* Вызывает RS_Send. Передаёт массив слов через SCI в выбранном режиме упаковки. */
RS_Send(rs_arr, rs_arr->buffer, length);
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Завершает receive_ump и возвращает 1. */
return 1;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Обслуживает чтение регистров FC03 с проверкой адреса, длины и формированием ответа RTU. */
void ReceiveCommandModbus3(RS_DATA *rs_arr)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет crc — данные crc; Address_MB — адрес MB; Length_MB — длина MB; i — данные i; value — данные value. */
unsigned int crc, Address_MB, Length_MB, i, value;
/* Проверяет if (receive_ump(rs_arr)) return; последующий оператор выполняется только при истинном условии. */
if (receive_ump(rs_arr)) return;
// получили начальный адрес чтениа
/* Записывает Address_MB значением (rs_arr->RS_Header[2] << 8) | rs_arr->RS_Header[3]; адрес MB. */
Address_MB = (rs_arr->RS_Header[2] << 8) | rs_arr->RS_Header[3];
// получили количество слов данных
/* Записывает Length_MB значением (rs_arr->RS_Header[4] << 8) | rs_arr->RS_Header[5]; длина MB. */
Length_MB = (rs_arr->RS_Header[4] << 8) | rs_arr->RS_Header[5];
/* Проверяет if (!Length_MB или Length_MB > 125 или; последующий оператор выполняется только при истинном условии. */
if (!Length_MB || Length_MB > 125 ||
/* Продолжает список аргументов, полей или выражение !(Address_MB < ANSWER_LEN && Length_MB <= ANSWER_LEN-Address_MB)) {; порядок элементов сохраняется. */
!(Address_MB < ANSWER_LEN && Length_MB <= ANSWER_LEN-Address_MB)) {
/* Вызывает modbus_exception. Формирует ответ Modbus с установленным старшим битом функции и кодом исключения. */
modbus_exception(rs_arr, CMD_MODBUS_3, 2); return;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/////////////////////////////////////////////////
// Отсылка
/* Посчитали контрольную сумму перед самой посылкой */
/* Записывает rs_arr->buffer[0] значением CNTRL_ADDR; rs arr буфер. */
rs_arr->buffer[0] = CNTRL_ADDR;
/* Записывает rs_arr->buffer[1] значением CMD_MODBUS_3; rs arr буфер. */
rs_arr->buffer[1] = CMD_MODBUS_3;
/* Записывает rs_arr->buffer[2] значением Length_MB*2; rs arr буфер. */
rs_arr->buffer[2] = Length_MB*2;
/* Штатные регистры копируются последовательно без блокировки IRQ вокруг всего блока.
* Измерительные ISR могут обновить соседние слова между чтениями; этот путь не гарантирует
* единый момент захвата всех значений. RAM-расширения захватываются отдельно в receive_ump. */
/* Формирует payload из запрошенного числа 16-битных слов; для каждого слова в ответе нужны два байта. */
for (i=0;i<Length_MB;i++)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Читает очередное слово один раз, чтобы старший и младший байты ответа относились к одному считанному значению. */
value = modbus[Address_MB+i];
/* Помещает старший байт слова после трёхбайтного заголовка; данные регистра передаются старшим байтом вперёд. */
rs_arr->buffer[3+i*2] = (value >> 8) & 0xFF;
/* Помещает младший байт непосредственно за старшим, сохраняя исходный 16-битный рисунок регистра. */
rs_arr->buffer[3+i*2+1] = value & 0xFF;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Записывает crc значением 0xffff; данные crc. */
crc = 0xffff;
/* Записывает crc значением get_crc_16(crc, rs_arr->buffer, Length_MB*2+3); данные crc. */
crc = get_crc_16(crc, rs_arr->buffer, Length_MB*2+3);
/* Записывает rs_arr->buffer[Length_MB*2+3] значением LOBYTE(crc); rs arr буфер. */
rs_arr->buffer[Length_MB*2+3] = LOBYTE(crc);
/* Записывает rs_arr->buffer[Length_MB*2+4] значением HIBYTE(crc); rs arr буфер. */
rs_arr->buffer[Length_MB*2+4] = HIBYTE(crc);
/* Первый исторический нулевой байт после CRC; входит в увеличенную длину штатного ответа. */
rs_arr->buffer[Length_MB*2+5] = 0;
/* Второй исторический нулевой байт после CRC; сохраняет прежний формат низких технологических адресов. */
rs_arr->buffer[Length_MB*2+6] = 0;
/* Третий исторический нулевой байт после CRC; это последний элемент передаваемого буфера при длине 2*N+8. */
rs_arr->buffer[Length_MB*2+7] = 0;
/* Дополнительный ноль находится за длиной, переданной в RS_Send; в текущий ответ это слово не отправляется. */
rs_arr->buffer[Length_MB*2+8] = 0;
/* Записывает rs_arr->flag_TIMEOUT_to_Send значением true; rs arr флаг TIMEOUT to Send. */
rs_arr->flag_TIMEOUT_to_Send=true;
/* Расширение логгера отвечает точным кадром Modbus без хвостовых
* нулей: они иначе попали бы в начало ответа следующего блока GUI.
* Исторический формат технологических регистров оставлен прежним. */
/* Вызывает RS_Send. Передаёт массив слов через SCI в выбранном режиме упаковки. */
RS_Send(rs_arr, rs_arr->buffer, Length_MB*2+8);
/* Завершает ReceiveCommandModbus3 без возвращаемого значения. */
return;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Обслуживает одиночную запись FC06, проверяя доступность регистра и допустимость значения. */
void ReceiveCommandModbus6(RS_DATA *rs_arr)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет Address_MB — адрес MB; Data_MB — данные MB; i — данные i. */
unsigned int Address_MB, Data_MB, i;
/* Проверяет if (receive_ump(rs_arr)) return; последующий оператор выполняется только при истинном условии. */
if (receive_ump(rs_arr)) return;
/////////////////////////////////////////////////
// Отсылка
/* Отправлаем назад то же самое */
/* Цикл for (i=0;i<8;i++); задаёт начальное значение, условие продолжения и шаг перебора. */
for (i=0;i<8;i++)
/* Записывает rs_arr->buffer[i] значением rs_arr->RS_Header[i]; rs arr буфер. */
rs_arr->buffer[i] = rs_arr->RS_Header[i];
// получили начальный адрес записи
/* Записывает Address_MB значением (rs_arr->RS_Header[2] << 8) | rs_arr->RS_Header[3]; адрес MB. */
Address_MB = (rs_arr->RS_Header[2] << 8) | rs_arr->RS_Header[3];
// получили слово данных
/* Записывает Data_MB значением (rs_arr->RS_Header[4] << 8) | rs_arr->RS_Header[5]; данные MB. */
Data_MB = (rs_arr->RS_Header[4] << 8) | rs_arr->RS_Header[5];
/* На плате LOAD запрещает запись только состояния защиты 0x7C; регистр лимита 0x7D принимает настройку. */
if (Desk == dsk_LOAD && Address_MB == 0x7CU) {
/* Вызывает modbus_exception. Формирует ответ Modbus с установленным старшим битом функции и кодом исключения. */
modbus_exception(rs_arr, CMD_MODBUS_6, 2); return;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Ноль в лимите 0x7D недопустим: первый пуск должен входить в разрешённые 1..65535 попыток. */
if (Desk == dsk_LOAD && Address_MB == 0x7DU && Data_MB == 0U) {
/* Возвращает код Modbus 3 для недопустимого значения, сохраняя прежний лимит и состояние автомата. */
modbus_exception(rs_arr, CMD_MODBUS_6, 3); return;
/* Завершает проверку ненулевого лимита перед общей записью технологического слова. */
}
/* Проверяет if (Desk == dsk_LOAD и Address_MB == 0x52U и; последующий оператор выполняется только при истинном условии. */
if (Desk == dsk_LOAD && Address_MB == 0x52U &&
/* Продолжает список аргументов, полей или выражение (Data_MB == 0U || Data_MB > 60000U)) {; порядок элементов сохраняется. */
(Data_MB == 0U || Data_MB > 60000U)) {
/* Вызывает modbus_exception. Формирует ответ Modbus с установленным старшим битом функции и кодом исключения. */
modbus_exception(rs_arr, CMD_MODBUS_6, 3); return;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Проверяет if (Address_MB < ANSWER_LEN); последующий оператор выполняется только при истинном условии. */
if (Address_MB < ANSWER_LEN) {
/* Записывает Modbus[Address_MB].all значением Data_MB; представление регистра целым словом без разбиения на битовые поля. */
Modbus[Address_MB].all = Data_MB;
/* Проверяет if (Address_MB == 127) ump_log_rs(); последующий оператор выполняется только при истинном условии. */
if (Address_MB == 127) ump_log_rs();
/* Выполняет альтернативу, если предыдущее условие if оказалось ложным. */
} else { modbus_exception(rs_arr, CMD_MODBUS_6, 2); return; }
/* Записывает rs_arr->flag_TIMEOUT_to_Send значением true; rs arr флаг TIMEOUT to Send. */
rs_arr->flag_TIMEOUT_to_Send=true;
/* Вызывает RS_Send. Передаёт массив слов через SCI в выбранном режиме упаковки. */
RS_Send(rs_arr, rs_arr->buffer, 10);
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Начинает функцию SendCommandModbus4; назначение модуля — обработка Modbus, заводские параметры и сохранение настроек. */
void SendCommandModbus4(RS_DATA *rs_arr)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет crc — данные crc. */
unsigned int crc;
/////////////////////////////////////////////////
// Отсылка
/* Записывает rs_arr->buffer[0] значением 16; rs arr буфер. */
rs_arr->buffer[0] = 16;
/* Записывает rs_arr->buffer[1] значением 4; rs arr буфер. */
rs_arr->buffer[1] = 4;
/* Записывает rs_arr->buffer[2] значением 0; rs arr буфер. */
rs_arr->buffer[2] = 0;
/* Записывает rs_arr->buffer[3] значением 0; rs arr буфер. */
rs_arr->buffer[3] = 0;
/* Записывает rs_arr->buffer[4] значением 0; rs arr буфер. */
rs_arr->buffer[4] = 0;
/* Записывает rs_arr->buffer[5] значением 48; rs arr буфер. */
rs_arr->buffer[5] = 48;
/* Записывает crc значением 0xffff; данные crc. */
crc = 0xffff;
/* Записывает crc значением get_crc_16(crc, rs_arr->buffer, 6); данные crc. */
crc = get_crc_16(crc, rs_arr->buffer, 6);
/* Записывает rs_arr->buffer[6] значением LOBYTE(crc); rs arr буфер. */
rs_arr->buffer[6] = LOBYTE(crc);
/* Записывает rs_arr->buffer[7] значением HIBYTE(crc); rs arr буфер. */
rs_arr->buffer[7] = HIBYTE(crc);
//rs_arr->buffer[8] = 0;
//rs_arr->buffer[9] = 0;
//rs_arr->buffer[10] = 0;
/* Записывает rs_arr->flag_TIMEOUT_to_Send значением true; rs arr флаг TIMEOUT to Send. */
rs_arr->flag_TIMEOUT_to_Send=true;
/* Вызывает RS_Send. Передаёт массив слов через SCI в выбранном режиме упаковки. */
RS_Send(rs_arr, rs_arr->buffer, 8);
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Начинает функцию ReceiveAnswerModbus4; назначение модуля — обработка Modbus, заводские параметры и сохранение настроек. */
void ReceiveAnswerModbus4(RS_DATA *rs_arr)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет i — данные i. */
unsigned int i;
/////////////////////////////////////////////////
// Отсылка
/* Цикл for (i=0;i<8;i++); задаёт начальное значение, условие продолжения и шаг перебора. */
for (i=0;i<8;i++)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Записывает ext_temp[i] значением ( rs_arr->RS_Header[5+i*12] << 8) +; данные ext_temp. */
ext_temp[i] = ( rs_arr->RS_Header[5+i*12] << 8) +
/* Продолжает список аргументов, полей или выражение rs_arr->RS_Header[6+i*12];; порядок элементов сохраняется. */
rs_arr->RS_Header[6+i*12];
/* Записывает ext_diag[i] значением ( rs_arr->RS_Header[7+i*12] |; данные ext_diag. */
ext_diag[i] = ( rs_arr->RS_Header[7+i*12] |
/* Продолжает список аргументов, полей или выражение rs_arr->RS_Header[8+i*12] ) != 0;; порядок элементов сохраняется. */
rs_arr->RS_Header[8+i*12] ) != 0;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
} }