/* Обработка 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;iadr_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;ibuffer[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;ibuffer[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; /* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */ } }