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

690 lines
54 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.
/* Обработка 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];
/* Заполняет регистры заводскими значениями; для LOAD задаёт тайм-аут свежести CAN 2000 мс. Значение 600 мс применяется отдельно при восстановлении недопустимого параметра. */
void Default_params()
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет i — данные i; def — данные def. */
unsigned int i,def;
/* Объявляет 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};
/* Цикл for(i=0;i<ANSWER_LEN;i++); задаёт начальное значение, условие продолжения и шаг перебора. */
for(i=0;i<ANSWER_LEN;i++)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Записывает Modbus[i].all значением 0; представление регистра целым словом без разбиения на битовые поля. */
Modbus[i].all = 0;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Записывает 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;
/* Инициализирует модуль вызовом Init_sensors_more(); перед его дальнейшим использованием. */
Init_sensors_more();
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Читает сохранённые регистры из области параметров EEPROM и применяет настройки платы. */
void Load_params()
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет i — данные i; crc — данные crc. */
unsigned int i,crc;
/* Вызывает 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]) )
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Цикл for(i=0;i<ANSWER_LEN;i++) modbus[i] = param[i];; задаёт начальное значение, условие продолжения и шаг перебора. */
for(i=0;i<ANSWER_LEN;i++) modbus[i] = param[i];
/* Записывает Commands значением 0; слово команд, технологический регистр 127. */
Commands=0;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Выполняет альтернативу, если предыдущее условие if оказалось ложным. */
else
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Восстанавливает заводские параметры после неверного CRC или несоответствия режима платы; LOAD_CAN_TIMEOUT получает 2000 мс. */
Default_params();
/* Записывает Commands значением 0; слово команд, технологический регистр 127. */
Commands=0;
/* Вызывает 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
/* Проверяет 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;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
} }