Перенести улучшения 166 и добавить сборку для M95M02 и Flash

Восстановить телеметрию CAN, обработку TESTLAMP и сохранение команд при загрузке параметров.

Выбрать M95M02 основным профилем, добавить самостоятельную Flash-сборку и проверять образы до публикации.

Проверки: хостовые тесты, реальные сборки TI C2000, контроль COFF/SCI8 и защита записи при активной нагрузке.
This commit is contained in:
2026-10-19 16:33:28 +03:00
parent 985804607d
commit bbd82b3a4e
42 changed files with 1820 additions and 321 deletions
+20
View File
@@ -65,6 +65,9 @@ static unsigned int load_state, load_attempts, load_inhibit;
static unsigned int load_count_dac, load_count_ramp, load_init_count;
/* Объявляет load_zero_sent — данные load_zero_sent; load_stop_asserted — признак установленного выхода STOP; load_initialized — данные load_initialized. */
static unsigned int load_zero_sent, load_stop_asserted, load_initialized;
#if defined(FLASH_BOOT) /* Дополнительный контроль записи Flash не увеличивает обычный EEPROM/RAM-образ. */
static unsigned int load_last_dac_code; /* Хранит именно последний переданный код SPI, отдельно от доступного для записи диагностического Modbus-регистра. */
#endif /* Приватное подтверждение передачи используется только автономным Flash-загрузчиком. */
/* Объявляет load_go — начальный ток за вычетом базовых 4 мА, ограниченный диапазоном 0..5; load_end — конечный ток за вычетом 4 мА, ограниченный сверху значением 16. */
static float load_go, load_end;
/* 16-битные поля общие с CAN ISR. Снимок и увеличение возраста выполняются
@@ -86,6 +89,9 @@ void Load_control_init(void)
load_count_dac = 0; load_count_ramp = 0; load_init_count = 1;
/* Записывает load_zero_sent значением 0; load_stop_asserted = 0; load_initialized = 0; данные load_zero_sent. */
load_zero_sent = 0; load_stop_asserted = 0; load_initialized = 0;
#if defined(FLASH_BOOT) /* Начальное значение не считается подтверждением безопасных 4 мА без load_zero_sent. */
load_last_dac_code = 0; /* Явно очищает COFF RAM; первое подтверждение будет получено только после штатного dSEND. */
#endif /* Завершает инициализацию дополнительного приватного контроля Flash. */
/* Записывает load_go значением 0; load_end = 0; начальный ток за вычетом базовых 4 мА, ограниченный диапазоном 0..5. */
load_go = 0; load_end = 0;
/* Записывает load_voltage значением 0; load_voltage_age = 0; load_voltage_seen = 0; последнее выбранное напряжение CAN, масштаб 1 В на слово. */
@@ -205,6 +211,17 @@ static void load_start(void)
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
#if defined(FLASH_BOOT) /* Проверка доступна только отдельному варианту с внутренней Flash и официальным TI API. */
int Load_flash_safe(void) /* Вызывается с заблокированными IRQ; не меняет команды, GPIO или алгоритм штатной остановки. */
{ /* Фиксирует согласованное состояние автомата, потому что вызывающий FlashRs_Command не разрешает Timer1 между проверкой и erase. */
if (Desk != dsk_LOAD) return 1; /* У других плат нет автомата аналоговой нагрузки; их PWM дополнительно блокирует автономный загрузчик. */
return load_initialized && READY && period_dac && time_dac && load_state == LOAD_IDLE && /* Требует завершённую инициализацию и штатное состояние ожидания вместо RUN/RELOAD/FAULT. */
load_stop_asserted && load_zero_sent && !IMPowse && /* STOP уже удержан не менее 1000 мс, а безопасный ток действительно передан штатным SPI-путём. */
!((unsigned int)Commands & 0x03C0U) && !cInitDac && /* START, RESET, калибровка и переинициализация ЦАП не должны создавать отложенные переходы во время прошивки. */
load_last_dac_code == ((unsigned int)DAC_min() & 0x0FFFU); /* Подтверждает программную передачу текущего кода 4 мА; это не измерение физического тока на плате. */
} /* После успешной проверки IRQ остаются заблокированы до автономной записи, поэтому разрешение не устаревает. */
#endif /* Обычная RAM/EEPROM-сборка не включает дополнительный публичный обработчик Flash. */
/* Вычисляет такты ЦАП; для недопустимого сохранённого тайм-аута CAN возвращает 600 мс. */
void Setup_DAC_time(void)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
@@ -317,6 +334,9 @@ static void anal_output(long vrot, long maxx, int calibration)
if(x) dSEND(out|0x0000|0x0000);
/* Выполняет альтернативу, если предыдущее условие if оказалось ложным. */
else dSEND(out|0x8000|0x0000);
#if defined(FLASH_BOOT) /* Снимок обновляется только после фактической отправки слова в SPI ЦАП. */
load_last_dac_code = (unsigned int)out; /* Приватное поле не может быть подменено обычной записью в технологический регистр 0x7B. */
#endif /* Завершает подтверждение последнего переданного уровня для проверки перед erase. */
/* Публикует фактически отправляемый 12-битный код ЦАП в технологическом регистре 0x7B для диагностики и регистрации. */
modbus[123] = out;
+3
View File
@@ -17,6 +17,9 @@ extern unsigned int period_dac, time_dac;
/* Call once before IRQ; complete technological CAN packets update voltage. */
/* Сбрасывает все переменные автомата нагрузки до разрешения прерываний; COFF не обнуляет всю .ebss. */
void Load_control_init(void);
#if defined(FLASH_BOOT) /* Только Flash-загрузчик использует проверку штатного безопасного состояния нагрузки. */
int Load_flash_safe(void); /* При запрещённых IRQ требует завершённый STOP, снятые команды и отправленный текущий код 4 мА; не выполняет аварийных записей GPIO. */
#endif /* RAM-вариант сохраняет прежний набор обработчиков и размер кода. */
/* Принимает напряжение выбранной нагрузки из достоверных слов CAN и сбрасывает возраст только нужного измерения. */
void Load_voltage_can(unsigned int header, unsigned int a,
/* Объявляет b — данные b. */
@@ -0,0 +1,123 @@
#ifndef CAN_TELEMETRY_IMPL_H /* Реализация включается только в ecan.c после аппаратных типов и переменных штатного передатчика. */
#define CAN_TELEMETRY_IMPL_H /* Не допускает повторного определения состояния и функций в одной единице трансляции. */
#define CAN_HEARTBEAT_ADDR 0x0100U /* Адрес heartbeat из реализации 166 находится вне технологической карты R0–R127 и служебной области логгера 0x1000. */
#define CAN_HEARTBEAT_TICKS ((Uint32)READY_FREQ) /* READY_FREQ задаёт частоту Timer1 в герцах; это ровно одна секунда его тактов. */
#define CAN_REFRESH_TICKS ((Uint32)READY_FREQ * 13UL / 10UL) /* Тринадцать десятых секунды дают период повторения данных 1,3 с независимо от числа циклов main. */
#define CAN_SCAN_TICKS ((Uint32)READY_FREQ * CANPOWSE / 1000UL) /* Существующие CANPOWSE=20 мс ограничивают задержку обнаружения обновлений, но не скорость выдачи уже готовой очереди. */
#define CAN_ACK_TICKS ((Uint32)READY_FREQ / 10UL) /* Программная страховка через 100 мс отменяет зависший TX, если аппаратный timeout IRQ не был обработан. */
static Uint32 can_last_hi[ANSWER_LEN], can_last_lo[ANSWER_LEN], can_last_at[ANSWER_LEN]; /* Последний подтверждённый пакет и время ACK хранятся отдельно для каждого начального адреса R0–R127. */
static unsigned int can_valid[8], can_cursor[2], can_stream; /* Восемь 16-битных масок покрывают 128 адресов; два курсора и очередность обеспечивают справедливость потоков. */
static Uint32 can_tx_at, can_failed_at, can_heartbeat_at, can_scan_at; /* Беззнаковые отметки Timer1 поддерживают вычитание через переполнение 32-битного счётчика. */
static unsigned int can_backoff, can_started, can_heartbeat_count, can_heartbeat_due, can_scan_due; /* Отделяет секундную паузу после ошибки, старт часов, heartbeat и незавершённое сканирование масок. */
static int can_pending_addr; /* Значение -1 обозначает heartbeat, который не должен индексировать кэш технологических регистров. */
static volatile unsigned int can_tx_failed; /* Timeout ISR отмечает ошибку текущего mailbox до обработки ACK в основном цикле. */
static Uint32 CAN_millis(void) /* Возвращает согласованный счётчик Timer1; при READY_FREQ=1000 один такт соответствует миллисекунде. */
{ /* Чтение 32 бит защищается отдельно на 16-битном C28x без ожидания аппаратуры. */
unsigned int saved = __disable_interrupts(); /* Сохраняет исходное состояние IRQ, включая вызовы из уже защищённой отправки. */
Uint32 now = CpuTimer1.InterruptCount; /* Счётчик обновляют оба существующих обработчика Timer1 для пульта и измерительной платы. */
__restore_interrupts(saved); /* Немедленно возвращает прежнюю маску прерываний. */
return now; /* Наружу передаётся один целостный снимок времени. */
} /* Длительные расчёты масок выполняются с разрешёнными прерываниями. */
static void CAN_telemetry_init(void) /* Сбрасывает программное состояние при каждом InitCan, в том числе после запуска с грязной RAM. */
{ /* Крупные массивы не очищаются, потому что их элементы недоступны без valid-бита. */
unsigned int i; /* Перебирает восемь масок достоверности кэша. */
for (i = 0U; i < 8U; ++i) can_valid[i] = 0U; /* Первый пакет каждого разрешённого адреса обязательно передаётся после запуска. */
can_cursor[0] = can_cursor[1] = can_stream = 0U; /* Обе маски начинают обход с R0; далее потоки чередуются после реально поставленного кадра. */
can_backoff = can_started = can_heartbeat_count = can_heartbeat_due = 0U; /* Новые часы и секундный heartbeat начинают отсчёт при первом обслуживании main. */
can_scan_due = 1U; /* Первый снимок данных выдаётся сразу, без ожидания периода 1,3 с. */
can_pending_addr = -1; can_tx_failed = 0U; wait = 0; /* Исключает подтверждение кадра и ошибку из предыдущего запуска программы. */
} /* Сброс не меняет аппаратные CAN ID, маски регистров и настройки EEPROM. */
static int CAN_tx_ready(Uint32 now) /* Обслуживает завершение штатного mailbox 0; вызывающий код уже запретил IRQ на короткое время. */
{ /* Возвращает готовность без ожидания ACK, CANAA или освобождения шины в цикле. */
if ((ECanaRegs.CANTRS.all | ECanaRegs.CANTRR.all) & 1UL) { /* Бит 0 относится только к штатному TX; служебный mailbox 2 не перезаписывается. */
if (wait && !can_tx_failed && (ECanaRegs.CANTRS.all & 1UL) && now - can_tx_at >= CAN_ACK_TICKS) { /* Программный timeout страхует потерянное аппаратное прерывание спустя 100 мс. */
can_tx_failed = 1U; ++CanTimeOutErrorTR; /* Отмечает ошибку ровно один раз, пока этот запрос ещё ожидает завершения отмены. */
EALLOW; ECanaRegs.CANTOC.all &= ~1UL; EDIS; /* Снимает источник аппаратного timeout только для mailbox 0, сохраняя остальные биты контроля. */
ECanaRegs.CANTRR.all = 1UL; /* Запрашивает аппаратную отмену без блокирующего ожидания CANAA. */
} /* Данные занятого mailbox остаются неизменными до аппаратного освобождения. */
return 0; /* Текущий адрес и признак готовности сканирования сохраняются для повторного обслуживания. */
} /* Только свободный mailbox позволяет обработать завершение предыдущей передачи. */
if (wait) { /* ACK или ошибка относятся ровно к одному ранее запущенному кадру. */
if ((ECanaRegs.CANTA.all & 1UL) && !can_tx_failed) { /* Только успешный ACK позволяет подавлять повтор неизменившихся данных. */
if (can_pending_addr >= 0) { /* Heartbeat не занимает ограниченный технологический кэш. */
can_last_hi[can_pending_addr] = ECanaMboxes.MBOX0.MDH.all; /* Запоминает действительно доставленные адрес и первое слово из неизменявшегося mailbox. */
can_last_lo[can_pending_addr] = ECanaMboxes.MBOX0.MDL.all; /* Сохраняет второе и третье слова того же подтверждённого кадра. */
can_last_at[can_pending_addr] = now; /* Отсчитывает повтор через 1,3 с от последнего подтверждения, а не от неудачной попытки. */
can_valid[can_pending_addr >> 4] |= 1U << (can_pending_addr & 15); /* Устанавливает один бит достоверности начального адреса пакета. */
} /* Перекрывающиеся пакеты разных масок имеют независимые начальные адреса. */
} else { /* Отмена, ошибка или свободный TX без CANAA не подтверждают доставку текущих данных. */
can_failed_at = now; can_backoff = 1U; /* Ограничивает новые попытки одной секундой при отсутствии получателя и CAN ACK. */
} /* Старый подтверждённый кэш сохраняется, поэтому неудачная передача не теряет обновление. */
wait = 0; can_tx_failed = 0U; can_pending_addr = -1; /* Завершение обрабатывается один раз даже без отдельного CANAA после отмены. */
} /* Проверяет паузу после ошибки до подготовки следующего кадра. */
if (can_backoff && now - can_failed_at < CAN_HEARTBEAT_TICKS) return 0; /* Во время секундной паузы данные и heartbeat остаются ожидающими. */
can_backoff = 0U; /* Снятие паузы не требует изменения таймеров технологических данных. */
return 1; /* Mailbox свободен и может принять один новый актуальный кадр. */
} /* Чужие mailbox, аппаратный CAN ID и получаемые команды не меняются. */
int CAN_send(int Port, int data[], int Addr) /* Возвращает 0 при занятости, 1 после запуска TX и 2 при неизменном пакете до срока повторения. */
{ /* Снимок данных и запуск mailbox защищаются одним коротким запретом IRQ. */
Uint32 hiword, loword, now; /* Ровно два 32-битных поля соответствуют восьми октетам существующего формата BALZAM. */
unsigned int saved = __disable_interrupts(); /* Исключает разрыв трёхсловного снимка и гонку запуска с timeout ISR. */
(void)Port; now = CAN_millis(); /* Как прежний передатчик, использует eCAN-A; время берётся из общего Timer1. */
if (!CAN_tx_ready(now)) { __restore_interrupts(saved); return 0; } /* При занятости ничего не перезаписывает и не подтверждает данные программно. */
if (Addr == CAN_HEARTBEAT_ADDR) { /* Отдельный пакет состояния узла не обращается к data=NULL или к R256. */
hiword = 0xE0000000UL | ((Uint32)CAN_HEARTBEAT_ADDR << 16) | can_heartbeat_count; /* Сохраняет маркер BALZAM и передаёт циклический 16-битный счётчик секунд. */
loword = ((Uint32)(unsigned int)Mode << 16) | (unsigned int)Desk; /* Два следующих слова сообщают аппаратный Mode и тип платы Desk. */
can_pending_addr = -1; /* Успешный heartbeat не подтверждает ни один технологический пакет. */
} else { /* Первый снимок, изменение любого слова либо истечение периода вызывают одинаковую отправку данных. */
if (Addr < 0 || Addr >= ANSWER_LEN) { __restore_interrupts(saved); return 2; } /* Не допускает чтения за картой R0–R127 и позволяет планировщику пропустить неверный адрес. */
hiword = 0xE0000000UL | ((Uint32)Addr << 16) | ((Uint32)data[Addr] & 0xFFFFUL); /* Первый регистр занимает младшие шестнадцать бит старшей половины CAN-данных. */
loword = Addr + 1 < ANSWER_LEN ? ((Uint32)data[Addr + 1] & 0xFFFFUL) << 16 : 0UL; /* Отсутствующее второе слово у конца карты заменяется нулём. */
if (Addr + 2 < ANSWER_LEN) loword |= (Uint32)data[Addr + 2] & 0xFFFFUL; /* Третье слово дополняется только в пределах реального массива регистров. */
if ((can_valid[Addr >> 4] & (1U << (Addr & 15))) && can_last_hi[Addr] == hiword && can_last_lo[Addr] == loword && now - can_last_at[Addr] < CAN_REFRESH_TICKS) { /* Совпадение всех слов подавляет передачу только до индивидуального срока 1,3 с. */
__restore_interrupts(saved); return 2; /* Не сдвигает время подтверждения: неизменные данные всё равно повторятся по таймеру. */
} /* Изменение данных не ждёт окончания периода повторения. */
can_pending_addr = Addr; /* Подготовленное состояние попадёт в кэш только после успешного аппаратного ACK. */
} /* Для heartbeat и данных используется один и тот же безопасный жизненный цикл TX. */
ECanaRegs.CANTA.all = 1UL; ECanaRegs.CANAA.all = 1UL; /* Очищает старые ACK и отмену mailbox 0 записью единиц в соответствующие биты eCAN. */
ECanaMboxes.MBOX0.MDH.all = hiword; ECanaMboxes.MBOX0.MDL.all = loword; /* Записывает замороженный трёхсловный снимок без последующей подмены во время ожидания ACK. */
EALLOW; ECanaRegs.CANTOC.all &= ~1UL; ECanaRegs.CANTSC = 0UL; ECanaRegs.CANTOC.all |= 1UL; EDIS; /* Перезапускает аппаратный timeout только для нового штатного запроса TX. */
can_tx_at = now; can_tx_failed = 0U; wait = 1; /* Сохраняет момент запуска и принадлежность будущего ACK до разрешения прерываний. */
ECanaRegs.CANTRS.all = 1UL; /* Однократно запускает аппаратную передачу в штатном mailbox 0. */
led1_toggle(); __restore_interrupts(saved); /* Сохраняет индикацию передачи и возвращает исходную маску IRQ без ожидания шины. */
return 1; /* Подтверждает только постановку кадра, оставляя окончательную доставку аппаратному ACK. */
} /* Формат CAN ID, порядок слов и служебный mailbox логгера остаются прежними. */
void CAN_heartbeat_service(void) /* Обслуживает независимый секундный heartbeat и завершение ранее запущенного TX. */
{ /* Вызывается из main и не зависит от Cancount, масок данных или признака CanGO. */
Uint32 now = CAN_millis(), elapsed, seconds; /* Время Timer1 учитывает задержки main без накопления дрейфа секундной сетки. */
unsigned int saved = __disable_interrupts(); /* Короткая обработка ACK синхронизируется с timeout ISR. */
CAN_tx_ready(now); __restore_interrupts(saved); /* Сохраняет успешное завершение данных даже тогда, когда новых пакетов пока нет. */
if (!can_started) { can_started = 1U; can_heartbeat_at = can_scan_at = now; } /* Первый heartbeat появляется спустя полную секунду от начала обслуживания текущего запуска. */
elapsed = now - can_heartbeat_at; /* Беззнаковое вычитание корректно при переполнении 32-битного счётчика. */
if (elapsed >= CAN_HEARTBEAT_TICKS) { /* Формирует новую отметку на каждой секундной границе Timer1. */
seconds = elapsed / CAN_HEARTBEAT_TICKS; can_heartbeat_at += seconds * CAN_HEARTBEAT_TICKS; /* После паузы main оставляет последнее значение без очереди устаревших кадров. */
can_heartbeat_count = (unsigned int)((can_heartbeat_count + seconds) & 0xFFFFUL); can_heartbeat_due = 1U; /* Счётчик heartbeat циклически проходит диапазон 0..65535 секунд. */
} /* Занятый передатчик не теряет ожидающий heartbeat. */
if (can_heartbeat_due && CAN_send(0, 0, CAN_HEARTBEAT_ADDR) == 1) can_heartbeat_due = 0U; /* Приоритетно ставит один актуальный heartbeat после освобождения mailbox. */
} /* Следующий повтор heartbeat планируется секундной сеткой, а не длительностью обхода масок. */
void CAN_telemetry_service(void) /* Передаёт изменившиеся данные и повторяет каждый подтверждённый пакет через 1,3 с. */
{ /* Фоновое обслуживание не ждёт CAN ACK и выдаёт готовую очередь по мере освобождения mailbox. */
Uint32 now; unsigned int attempt, stream, pass, address, cursor; int result; /* Локальные счётчики ограничивают обход двумя потоками и конечной картой из 128 регистров. */
CAN_heartbeat_service(); now = CAN_millis(); /* Секундный heartbeat получает приоритет перед обычными данными независимо от настройки потоков. */
if (now - can_scan_at >= CAN_SCAN_TICKS) { can_scan_at += (now - can_scan_at) / CAN_SCAN_TICKS * CAN_SCAN_TICKS; can_scan_due = 1U; } /* Сохраняет сетку обнаружения изменений с интервалом 20 мс без накопления дрейфа после задержек main. */
if (!can_scan_due) return; /* При неизменных данных main не повторяет полный обход масок на каждой итерации. */
for (attempt = 0U; attempt < 2U; ++attempt) { /* После успешной постановки чередует потоки, исключая голодание медленной маски. */
stream = (can_stream + attempt) & 1U; cursor = can_cursor[stream]; /* Начинает следующий поиск после последнего принятого кадра данного потока. */
if (!Cancount[stream]) continue; /* Ноль сохраняет штатное отключение соответствующего потока; ненулевой период заменяется общим повтором 1,3 с. */
for (pass = 0U; pass < 2U; ++pass) { /* Два конечных прохода реализуют циклический обход без зависания на пустой маске. */
for (address = 0U; address < ANSWER_LEN;) { /* Формирует те же трёхсловные группы от первого разрешённого адреса маски. */
if (!(Maska[stream][address >> 4] & (1U << (address & 15)))) { ++address; continue; } /* Пропускает запрещённые регистры и гарантированно продвигает поиск. */
if ((pass == 0U && address >= cursor) || (pass == 1U && address < cursor)) { /* Сначала рассматривает хвост карты после курсора, затем оставшееся начало. */
result = CAN_send(0, modbus, address); /* Сравнивает полный снимок с подтверждённым кэшем и индивидуальным временем повтора. */
if (result != 2) { /* Только неизменный пакет до срока допускает переход к следующему кандидату в этом же вызове. */
if (result == 1) { can_cursor[stream] = address + 3U < ANSWER_LEN ? address + 3U : 0U; can_stream = stream ^ 1U; } /* Продвигает курсор лишь после постановки кадра и отдаёт следующий свободный слот другому потоку. */
return; /* При занятости сохраняет адрес; при постановке продолжит готовую очередь следующей итерацией main после ACK. */
} /* Пропущенные неизменные пакеты не занимают физическую шину. */
} /* Завершает проверку одного допустимого начального адреса. */
address += 3U; /* Как прежний протокол, один кадр покрывает три последовательных регистра независимо от разреженности маски. */
} /* Максимальный адрес ограничен размером реальной технологической карты. */
} /* После обхода обеих частей все пакеты потока рассмотрены ровно один раз. */
} /* Ни один разрешённый пакет не требует немедленной передачи. */
can_scan_due = 0U; /* Следующее плановое сканирование произойдёт через 20 мс; выдача готовой очереди этим ожиданием не задерживается. */
} /* Период 1,3 с относится к каждому пакету, а не к одному кадру всей маски за один период. */
#endif /* Закрывает единственную реализацию фоновой телеметрии текущей прошивки 166. */
+6 -2
View File
@@ -1,8 +1,12 @@
/* Штатный eCAN и отдельный служебный обмен УМП/GAS. Русские пояснения сохранены в UTF-8. */
/* Настраивает eCAN, скорость, почтовые ящики и маршрутизацию прерываний для выбранного режима платы. */
void InitCan(int Port, int DevNum);
/* Упаковывает адрес и технологические слова в штатный CAN-кадр и запускает передачу mailbox. */
void CAN_send(int Port, int data[], int Addr);
/* Возвращает 0 при занятости mailbox, 1 при запуске кадра, 2 для неизменного пакета до индивидуального повтора 1,3 с. */
int CAN_send(int Port, int data[], int Addr);
/* Поддерживает секундный heartbeat независимо от масок и подтверждает доставку предыдущего технологического кадра. */
void CAN_heartbeat_service(void);
/* Передаёт изменившиеся данные и повторяет подтверждённые пакеты через 1,3 с, не ожидая CAN ACK в главном цикле. */
void CAN_telemetry_service(void);
/* Объявляет CAN_input_data — CAN вход данные. Память определена в другом модуле. */
extern int CAN_input_data[];
+2 -2
View File
@@ -12,8 +12,8 @@
*/
/* Начинает условие: EEPROM_M95M02 ещё не определён; защищает повторное включение или задаёт значение сборки. */
#ifndef EEPROM_M95M02
/* Задаёт EEPROM_M95M02 = 0; выбор аппаратной EEPROM при сборке: 0 — AT25256B 32 КиБ, 1 — M95M02 256 КиБ и GAS. */
#define EEPROM_M95M02 0
/* Основная плата использует M95M02-DR ёмкостью 256 КиБ с областью GAS; старую AT25256B выбирают явно define=0 при отдельной сборке. */
#define EEPROM_M95M02 1
/* Закрывает условный блок препроцессора; дальнейшие строки относятся к внешнему уровню. */
#endif
/* Условие компиляции EEPROM_M95M02 != 0 и EEPROM_M95M02 != 1: выбранный блок включается в текущий вариант прошивки. */
+5
View File
@@ -0,0 +1,5 @@
#ifndef BALSAM_FLASH_RS_H /* Исключает повторное объявление интерфейса обновления внутренней Flash. */
#define BALSAM_FLASH_RS_H /* Помечает уже подключённый интерфейс RS-загрузчика. */
#include "RS485.h" /* Передаёт выбранный SCI-порт и сведения о проверенном блоке из действующего загрузчика. */
void FlashRs_Command(RS_DATA *rs); /* Выполняет пробу возможностей или запись Flash; после стирания никогда не возвращается в прежний код. */
#endif /* Завершает защиту заголовка обновления через RS. */
+1 -1
View File
@@ -13,7 +13,7 @@
* Дополнительные области GAS присутствуют только при установленной M95M02.
*/
/* Условие компиляции EEPROM_M95M02: выбранный блок включается в текущий вариант прошивки. */
#if EEPROM_M95M02
#if EEPROM_M95M02 || defined(FLASH_BOOT) /* Flash-вариант тоже публикует GAS; физический профиль EEPROM по-прежнему выбирается независимо. */
/* Задаёт GAS167_REG_BASE = 0x5000U; начало псевдонимов 128 технологических регистров GAS, адрес 0x5000. */
#define GAS167_REG_BASE 0x5000U
/* Задаёт GAS167_REG_COUNT = 128U; число технологических слов GAS; скрытое слово modbus[128] в диапазон не входит. */
+2 -2
View File
@@ -181,8 +181,8 @@
/* Сохраняет прежний заводской предел пяти пусков для сброса настроек и замены нуля из старой EEPROM. */
#define LOAD_DEFAULT_MAX_ATTEMPTS 5U
/* Задаёт Cancount = (modbus+0x60); данные Cancount. */
#define Cancount (modbus+0x60) // пауза между I посылками CAN
/* Регистры R96/R97 включают быстрый и медленный потоки CAN: 0 отключает поток, любое ненулевое значение разрешает изменения и индивидуальный повтор каждого пакета через 1,3 с. */
#define Cancount (modbus+0x60) /* Сохраняет адреса настроек EEPROM и Modbus; интервалы задаёт аппаратное время в can_telemetry_impl.h. */
/* Задаёт Bright = (modbus+0x62); данные Bright. */
#define Bright (modbus+0x62) // аркость сигнальных лампочек
/* Задаёт Brightness = modbus[0x62]; данные Brightness. */
+17
View File
@@ -11,6 +11,9 @@
#include "spise2p.h"
/* Подключает "log_to_mem.h": двухбанковый регистратор событий нагрузки во внешней RAM. */
#include "log_to_mem.h"
#if defined(FLASH_BOOT) /* Только отдельный Flash-образ содержит официальный алгоритм программирования, выполняемый из RAM. */
#include "flash_rs.h" /* Передаёт выбранный SCI и сведения о CRC-подтверждённом LOAD в автономный Flash-загрузчик. */
#endif /* RAM/EEPROM-вариант сохраняет прежний объём без подключения TI Flash API. */
//#include "flash_tools.h"
//#include "spartan_tools.h"
//#include "big_dsp_module.h"
@@ -524,6 +527,20 @@ void xflash(RS_DATA *rs_arr)
void tflash(RS_DATA *rs_arr)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
#if defined(FLASH_BOOT) /* Разрешает внутреннюю Flash только в образе с отдельными секциями LOAD/RUN и TI API. */
FlashRs_Command(rs_arr); /* Нулевой запрос проверяет поддержку; ненулевой программирует только полностью проверенную таблицу Flash SCI8. */
#else /* Обычная прошивка явно сообщает отсутствие Flash-загрузчика вместо молчаливого игнорирования команды 55. */
unsigned int crc; /* Контрольная сумма защищает адрес, CMD_TFLASH и двухоктетный статус отказа. */
rs_arr->buffer[0] = rs_arr->addr_recive; /* Ответ относится к фактическому адресу запроса, включая служебный адрес платы. */
rs_arr->buffer[1] = CMD_TFLASH; /* Сохраняет номер 55 существующего BIOS-протокола и таблицы длин. */
rs_arr->buffer[2] = 1U; /* Младшая октета статуса 0x1001 означает отсутствие Flash API в RAM/EEPROM-сборке. */
rs_arr->buffer[3] = 0x10U; /* Старшая октета отличает отказ загрузчика от штатных кодов официального TI API. */
crc = get_crc_16(0xFFFFU, rs_arr->buffer, 4); /* Использует Modbus CRC16 с начальным 0xFFFF для первых четырёх октет ответа. */
rs_arr->buffer[4] = LOBYTE(crc); /* CRC передаётся младшей октетой вперёд, как в остальных BIOS-командах. */
rs_arr->buffer[5] = HIBYTE(crc); /* Завершает защищённую часть кадра старшей октетой CRC. */
rs_arr->buffer[6] = rs_arr->buffer[7] = 0; /* Сохраняет два исторических завершающих нуля короткого BIOS-ответа. */
RS_Send(rs_arr, rs_arr->buffer, 8); /* Передаёт явный отказ без обращения к EEPROM, RAM-буферу загрузки или внутренней Flash. */
#endif /* Завершает выбор обработчика по типу исполняемого образа. */
// volatile unsigned long Address1,Address2;
// volatile unsigned long Length, LengthW;
/*
+7 -73
View File
@@ -75,6 +75,8 @@ static unsigned int ump_can_waiting;
/* Объявляет ump_can_sent_at — время последней передачи фрагмента ответа CAN. */
static unsigned long ump_can_sent_at;
/* Подключает планировщик после объявления wait: оба CAN-канала сохраняют независимое состояние и тайм-ауты. */
#include "can_telemetry_impl.h"
/* Сбрасывает служебный обмен до включения CAN: загрузка COFF не гарантирует обнуление неинициализированной RAM. */
static void ump_can_init(void)
/* Начинает сброс состояния; InitCan вызывается при запрещённых глобальных прерываниях. */
@@ -118,6 +120,7 @@ void InitCan(int Port, int DevNum)
long id = 0x80BA0000;
/* Обнуляет состояние служебного обмена до настройки mailbox и разрешения прерываний CAN. */
CAN_telemetry_init(); /* Сбрасывает ACK-кэш, курсоры, heartbeat и wait до разрешения прерываний, включая перезапуск с остатками RAM. */
ump_can_init();
/* Изменяет счётчик в выражении DevNum--; на один для следующего шага. */
@@ -295,77 +298,7 @@ void InitCan(int Port, int DevNum)
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Упаковывает адрес и технологические слова в штатный CAN-кадр и запускает передачу mailbox. */
void CAN_send(int Port, int data[], int Addr)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет hiword — данные hiword; loword — данные loword. */
unsigned long hiword,loword;
/* Продолжает список аргументов, полей или выражение volatile struct ECAN_REGS * ECanRegs;; порядок элементов сохраняется. */
volatile struct ECAN_REGS * ECanRegs;
/* Продолжает список аргументов, полей или выражение volatile struct ECAN_MBOXES * ECanMboxes;; порядок элементов сохраняется. */
volatile struct ECAN_MBOXES * ECanMboxes;
/* Записывает ECanRegs значением &ECanaRegs; E CAN Regs. */
ECanRegs = &ECanaRegs;
/* Записывает ECanMboxes значением &ECanaMboxes; E CAN Mboxes. */
ECanMboxes = &ECanaMboxes;
/* Не меняет данные mailbox 0, пока передача либо её аппаратная отмена ещё выполняется; бит 0 соответствует штатному TX. */
if ((ECanRegs->CANTRS.all | ECanRegs->CANTRR.all) & 1UL)
return; /* Сохраняет текущий кадр до завершения CANTRS/CANTRR, не ожидая аппаратного ответа в главном цикле. */
/* Проверяет if(wait); последующий оператор выполняется только при истинном условии. */
if(wait)
/* Проверяет if(!(ECanRegs->CANTA.all & 1)); последующий оператор выполняется только при истинном условии. */
if(!(ECanRegs->CANTA.all & 1))
/* Проверяет if(!(ECanRegs->CANAA.all & 1)); последующий оператор выполняется только при истинном условии. */
if(!(ECanRegs->CANAA.all & 1))
/* Завершает CAN_send без возвращаемого значения. */
return;
/* Записывает ECanRegs->CANTA.all значением 1; представление регистра целым словом без разбиения на битовые поля. */
ECanRegs->CANTA.all = 1;
/* Записывает ECanRegs->CANAA.all значением 1; представление регистра целым словом без разбиения на битовые поля. */
ECanRegs->CANAA.all = 1;
/* Записывает hiword значением ((((Uint32) Addr ) & 0xffff)<<16)| 0xE0000000 |; данные hiword. */
hiword= ((((Uint32) Addr ) & 0xffff)<<16)| 0xE0000000 |
/* Продолжает список аргументов, полей или выражение ((((Uint32)data[Addr ]) & 0xffff) );; порядок элементов сохраняется. */
((((Uint32)data[Addr ]) & 0xffff) );
/* Записывает loword значением ((((Uint32)data[Addr+1]) & 0xffff)<<16)|; данные loword. */
loword= ((((Uint32)data[Addr+1]) & 0xffff)<<16)|
/* Продолжает список аргументов, полей или выражение ((((Uint32)data[Addr+2]) & 0xffff) );; порядок элементов сохраняется. */
((((Uint32)data[Addr+2]) & 0xffff) );
/* Записывает ECanMboxes->MBOX0.MDH.all значением hiword; представление регистра целым словом без разбиения на битовые поля. */
ECanMboxes->MBOX0.MDH.all = hiword;
/* Записывает ECanMboxes->MBOX0.MDL.all значением loword; представление регистра целым словом без разбиения на битовые поля. */
ECanMboxes->MBOX0.MDL.all = loword;
/* Разрешение записи в защищённые регистры периферии. */
EALLOW;
/* Отключает старый контроль mailbox 0 перед перезапуском счётчика; остальные mailbox используют собственный контроль. */
ECanRegs->CANTOC.all = 0;
/* Записывает ECanRegs->CANTSC значением 0; E CAN Regs CANTSC. */
ECanRegs->CANTSC = 0; // clear time-out counter
/* Заново разрешает тайм-аут штатного TX с порогом MOTO0, установленным в InitCan, только для следующего кадра. */
ECanRegs->CANTOC.all = 1UL;
/* Закрытие доступа к защищённым регистрам периферии. */
EDIS;
/* Записывает ECanRegs->CANTRS.all значением 1; представление регистра целым словом без разбиения на битовые поля. */
ECanRegs->CANTRS.all = 1; // запустить передачу
/* Записывает wait значением 1; данные wait. */
wait=1;
/* Вызывает led1_toggle с аргументами () в текущей последовательности операций. */
led1_toggle();
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Начинает функцию Handlai; назначение модуля — штатный eCAN и отдельный служебный обмен УМП/GAS. */
/* Разделяет служебные запросы УМП и технологические команды CAN, сохраняя защиту нагрузки и журналирование версии 167. */
void Handlai(volatile struct ECAN_MBOXES * ECanMboxes)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
@@ -497,10 +430,11 @@ interrupt void CANa_reset_err(void)
if (ECanaRegs.CANTOS.all & 1UL)
{ /* Обрабатывает только штатный передатчик; служебный mailbox 2 контролируется отдельно в UMP_CAN_service. */
EALLOW; /* Разрешает изменение управляющего регистра eCAN перед снятием источника прерывания. */
ECanaRegs.CANTOC.all = 0; /* Сброс TOC снимает TOS и MTOF согласно SPRUI07, 13.8.18.3; одной отмены TX недостаточно. */
ECanaRegs.CANTOC.all &= ~1UL; /* Снимает источник timeout только MBOX0 по SPRUI07 13.8.18.3; остальные каналы сохраняют свой контроль. */
EDIS; /* Восстанавливает защиту регистров до выхода из ветви обработки тайм-аута. */
if (ECanaRegs.CANTRS.all & 1UL) /* Передача могла завершиться между чтением TOS и снятием контроля. */
ECanaRegs.CANTRR.all = 1UL; /* Запрашивает отмену только ещё занятого mailbox 0, не задерживая прерывание ожиданием CANAA. */
can_tx_failed = 1U; /* Не позволяет отложенному ACK после тайм-аута подтвердить недоставленный снимок и подавить повтор данных. */
CanTimeOutErrorTR++; /* Считает обнаруженный тайм-аут один раз, исключая фантомные повторные входы PIE. */
} /* Следующая штатная передача сама заново включит TOC после сброса временного счётчика. */
@@ -575,7 +509,7 @@ void UMP_CAN_service(void)
/* Записывает ump_can_length значением Load_protection_prepare(tag & 0xFF, address, value, ump_can_reply, &error); ump CAN длина. */
ump_can_length = Load_protection_prepare(tag & 0xFF, address, value, ump_can_reply, &error);
/* Условие компиляции EEPROM_M95M02: выбранный блок включается в текущий вариант прошивки. */
#if EEPROM_M95M02
#if EEPROM_M95M02 || defined(FLASH_BOOT) /* Отдельный Flash-вариант использует тот же GAS provider и согласованный снимок служебного CAN. */
/* Проверяет else if (gas167_contains(address)); последующий оператор выполняется только при истинном условии. */
else if (gas167_contains(address))
/* Записывает ump_can_length значением gas167_prepare(tag & 0xFF, address, value, 1, ump_can_reply, &error); ump CAN длина. */
+12
View File
@@ -0,0 +1,12 @@
.ref _c_int00 ; Стандартный COFF startup применяет .cinit и вызывает main после отключения раннего watchdog.
.sect "codestart" ; Boot ROM передаёт управление двухсловному переходу по аппаратному адресу слова 0x33FFF6.
LB flash_boot_start ; Переходит к короткой подготовке C28x во Flash, сохраняя точный размер аппаратного входа два слова.
.text ; Последовательность взята из штатного TI DSP2833x_CodeStartBranch.asm и размещается в секторе основного кода.
flash_boot_start: ; Отключает watchdog до возможной длительной обработки таблиц инициализации C, а не только после входа в main.
SETC OBJMODE ; Выбирает C28x object mode, требуемый стандартным COFF startup процессора F28335.
EALLOW ; Разрешает запись защищённого регистра WDCR перед запуском C runtime.
MOVZ DP, #7029h>>6 ; Адрес слова WDCR=0x7029 из карты F28335; DP выбирает соответствующую страницу по 64 слова.
MOV @7029h, #0068h ; Значение 0x0068 содержит контрольные биты WDCHK=101 и WDDIS=1, как официальный TI startup.
EDIS ; Возвращает защиту системных регистров перед переходом в библиотечную инициализацию.
LB _c_int00 ; Применяет штатную ROM-модель инициализации и затем запускает текущий main 167.
.end ; Завершает модуль, не добавляя произвольных данных рядом с CSM-паролями сектора A.
+179
View File
@@ -0,0 +1,179 @@
#if defined(FLASH_BOOT) /* Подключает Flash API только к образу, исполняемому из внутренней Flash. */
#include "DSP2833x_Device.h" /* Предоставляет регистры SCI, watchdog, CSM и действующую частоту SYSCLKOUT в герцах. */
#include "flash_rs.h" /* Связывает алгоритм с существующим загрузчиком RAM и выбранным RS-портом. */
#include "bios_dsp.h" /* Использует неизменный номер CMD_TFLASH действующего BIOS-протокола. */
#include "DAC.h" /* Проверяет завершённую штатную остановку нагрузки до отключения Timer1 на время стирания. */
#include "Flash2833x_API_Library.h" /* Вызывает официальный алгоритм TI v2.10 для стирания, записи и проверки F28335. */
#include <string.h> /* Переносит код и константы Flash API во внутреннюю RAM до изменения Flash. */
extern Uint16 FlashRsLoadStart, FlashRsLoadEnd, FlashRsRunStart; /* Границы отдельной секции задаёт F28335_flash.cmd в адресуемых 16-битных словах. */
static Uint16 program_buffer[128]; /* Буфер на 128 слов хранится во внутренней RAM; он не зависит от стираемых секторов или порядка байтов RS. */
static FLASH_ST api_status; /* Внутренняя RAM сохраняет адрес и данные первой ошибки официального Flash API. */
static unsigned int flash_load_guard(unsigned long length) /* Проверяет разрешение записи до CRC/erase, сохраняя безопасную пробу поддержки с нулевой длиной. */
{ /* Делегирует состояние нагрузки её единственному владельцу, не дублируя управление GPIO внутри загрузчика. */
return length && !Load_flash_safe() ? 0x1005U : 0U; /* Статус 0x1005 требует штатного STOP и подтверждённых 4 мА; probe длины ноль никогда не останавливает работу нагрузки. */
} /* После положительной проверки вызывающий код удерживает IRQ заблокированными до автономной записи. */
#pragma CODE_SECTION(byte_at, "flash_rs") /* Размещает декодер упакованных RS-байтов в копируемой секции RAM. */
static Uint16 byte_at(const Uint16 *data, Uint32 offset) /* Читает октет из старшего байта чётного слова и младшего байта нечётного, как RS_RX_Handler. */
{ /* Начинает независимый от Flash декодер входного блока. */
return (offset & 1UL) ? (data[offset >> 1] & 255U) : (data[offset >> 1] >> 8); /* Преобразует упаковку RS в октеты без обращения к таблицам или runtime. */
} /* Завершает чтение одного октета. */
#pragma CODE_SECTION(word_at, "flash_rs") /* Делает декодирование формата SCI8 доступным после стирания Flash. */
static Uint16 word_at(const Uint16 *data, Uint32 offset) /* Читает слово TI boot table, в котором младший октет передаётся первым. */
{ /* Начинает объединение двух октетов. */
return byte_at(data, offset) | (byte_at(data, offset + 1UL) << 8); /* Восстанавливает 16-битное слово, сохраняя отличие от внутренней упаковки RS. */
} /* Завершает чтение слова SCI8. */
#pragma CODE_SECTION(long_at, "flash_rs") /* Декодирует адреса без обращения к коду стираемой программы. */
static Uint32 long_at(const Uint16 *data, Uint32 offset) /* Формат TI передаёт старшее 16-битное слово адреса перед младшим. */
{ /* Начинает чтение 32-битного адреса в словах C28x. */
return ((Uint32)word_at(data, offset) << 16) | word_at(data, offset + 2UL); /* Объединяет два слова без зависимости от порядка байтов процессора ПК. */
} /* Завершает декодирование адреса. */
#pragma CODE_SECTION(crc_byte, "flash_rs") /* Вычисляет CRC без таблицы crc16, расположенной в стираемом секторе G. */
static Uint16 crc_byte(Uint16 crc, Uint16 value) /* Продвигает стандартный CRC16 Modbus, используемый существующим RS-протоколом. */
{ /* Начинает обработку одного октета сообщения. */
Uint16 bit; /* Счётчик восьми битов октета не требует библиотечной арифметики. */
crc ^= value; /* Добавляет младший октет к состоянию отражённого CRC. */
for (bit = 0; bit < 8U; ++bit) /* Полином применяется ровно к восьми передаваемым битам. */
crc = (crc & 1U) ? ((crc >> 1) ^ 0xA001U) : (crc >> 1); /* 0xA001 — отражённый полином CRC16 Modbus, совпадающий с проверкой GUI. */
return crc; /* Возвращает состояние без вызовов функции из Flash. */
} /* Завершает шаг CRC. */
#pragma CODE_SECTION(reply_status, "flash_rs") /* Передаёт итог даже после удаления старой программы и её ISR. */
static void reply_status(RS_DATA *rs, Uint16 status) /* Ответ содержит адрес, команду, 16-битный статус и CRC; SCI и RS485 обслуживаются опросом. */
{ /* Начинает автономную передачу подтверждения из RAM. */
Uint16 frame[8], crc = 0xFFFFU, i; /* Шесть октетов кадра и два завершающих нуля соответствуют традиционным ответам BIOS. */
volatile Uint32 guard; /* Ограничивает ожидание SCI, чтобы отказ передатчика не блокировал завершение навсегда. */
frame[0] = rs->addr_recive; /* Отвечает на фактический адрес запроса, поддерживая обычный и сервисный адрес платы. */
frame[1] = CMD_TFLASH; /* Номер 55 сохраняет совместимость с таблицей длин существующего загрузчика. */
frame[2] = status & 255U; /* Передаёт младший октет результата TI API либо ошибки проверки образа. */
frame[3] = status >> 8; /* Передаёт старший октет статуса для различения ошибок загрузчика и API. */
for (i = 0; i < 4U; ++i) crc = crc_byte(crc, frame[i]); /* Контрольная сумма защищает и адрес, и конкретный результат записи. */
frame[4] = crc & 255U; /* CRC Modbus передаёт младший октет первым. */
frame[5] = crc >> 8; /* Передаёт старший октет контрольной суммы. */
frame[6] = frame[7] = 0; /* Сохраняет два завершающих нуля действующего RS-протокола. */
rs->SciRegs->SCICTL2.bit.TXINTENA = 0; /* Передача не должна вызывать ISR из удаляемой Flash. */
rs->SciRegs->SCICTL2.bit.RXBKINTENA = 0; /* Приём во время итогового ответа также не обращается к старому обработчику. */
rs->SciRegs->SCIFFTX.bit.TXFFIENA = 0; /* Отключает FIFO-прерывание для автономной передачи опросом. */
rs->SciRegs->SCIFFRX.bit.RXFFIENA = 0; /* Исключает FIFO RX ISR до восстановления обычного приёма или перезапуска. */
rs->SciRegs->SCICCR.bit.PARITYENA = 0; /* Ответ передаётся в режиме 8N1, принятом в обычном подтверждении BIOS. */
if (rs->commnumber == COM_2) GpioDataRegs.GPBDAT.bit.GPIO34 = 0; /* На этой плате низкий GPIO34 включает передатчик RS485, как RS_Line_to_send. */
for (i = 0; i < 8U; ++i) /* Последовательно отправляет кадр и завершающие байты без прерываний. */
{ /* Начинает ожидание свободного места в передатчике. */
guard = 10000000UL; /* Десять миллионов опросов дают запас на восемь байтов при штатных скоростях RS. */
while (!rs->SciRegs->SCICTL2.bit.TXRDY && --guard) {} /* Не переписывает SCITXBUF, пока предыдущее слово не принято передатчиком. */
if (!guard) break; /* Завершает попытку при неисправном или отключённом SCI. */
rs->SciRegs->SCITXBUF = frame[i]; /* Передаёт один восьмибитный октет по выбранному SCI-A или SCI-B. */
} /* Завершает отправку кадра. */
guard = 10000000UL; /* Ограничивает ожидание ухода последнего стопового бита тем же запасом. */
while ((rs->SciRegs->SCIFFTX.bit.TXFFST || !rs->SciRegs->SCICTL2.bit.TXEMPTY) && --guard) {} /* Для FIFO SCI-A ждёт также опустошения очереди, чтобы не потерять конец кадра при выключении RS485 или reset. */
if (rs->commnumber == COM_2) GpioDataRegs.GPBDAT.bit.GPIO34 = 1; /* Возвращает направление RS485 к приёму после завершения кадра. */
} /* Завершает автономный ответ. */
#pragma CODE_SECTION(validate_image, "flash_rs") /* Проверка таблицы также находится в RAM и не зависит от Flash API. */
static Uint16 validate_image(const Uint16 *data, Uint32 length) /* Проверяет весь образ до первого стирания, включая загрузочный переход и запрещённые адреса CSM. */
{ /* Начинает проверку структуры таблицы TI SCI8. */
Uint32 offset = 22UL, address, bytes, entry_address, prior, prior_address; /* Заголовок TI занимает 11 слов; дополнительные курсоры проверяют перекрытия и наличие данных по адресу входа. */
Uint16 count, boot = 0, entry = 0, prior_count; /* Требует единственный boot-переход и хотя бы один блок, содержащий точку входа C runtime. */
if (length < 24UL || (length & 1UL) || word_at(data, 0) != 0x08AAU) return 0x1002U; /* Ключ SCI8 и чётный размер исключают обычный плоский EEPROM BIN. */
address = long_at(data, 18UL); /* Адрес входа следует после восьми резервных слов заголовка TI. */
entry_address = address; /* Сохраняет адрес для проверки наличия соответствующих исполняемых слов в таблице. */
if (address < 0x300000UL || address >= 0x33FF80UL) return 0x1002U; /* Точка входа C runtime обязана находиться в исполняемой Flash вне CSM. */
while (offset + 2UL <= length) /* Читает блоки только в пределах подтверждённого загрузчиком массива. */
{ /* Начинает проверку очередного блока. */
count = word_at(data, offset); /* Размер блока выражен в 16-битных словах C28x, а не октетах RS. */
offset += 2UL; /* Продвигает курсор за поле длины. */
if (!count) return (offset == length && boot == 1U && entry) ? 0U : 0x1002U; /* Нулевой блок завершает образ; хвост, отсутствие boot-перехода или данных C runtime запрещены. */
if (offset + 4UL > length) return 0x1002U; /* Не читает обрезанный 32-битный адрес назначения. */
address = long_at(data, offset); /* Читает адрес в формате старшее слово, затем младшее. */
offset += 4UL; /* Переводит курсор к первому слову данных блока. */
bytes = (Uint32)count << 1; /* Каждое слово программы соответствует двум октетам файла. */
if (bytes > length - offset) return 0x1002U; /* Проверяет остаток файла без возможности выхода за границы массива. */
if (address == 0x33FFF6UL && count == 2U) ++boot; /* Единственное исключение рядом с CSM — аппаратный переход из двух слов. */
else if (address < 0x300000UL || address >= 0x33FF80UL || (Uint32)count > 0x33FF80UL - address) return 0x1002U; /* Запрещает OTP, RAM, пароли CSM и зарезервированный диапазон сектора A. */
if (entry_address >= address && entry_address < address + count) entry = 1U; /* Подтверждает, что после boot-перехода процессор получит код из текущего образа. */
prior = 22UL; /* Начинает сравнение диапазона с уже проверенными блоками таблицы. */
while (prior < offset - 6UL) /* Повторно рассматривает только заголовки, прошедшие проверки границ выше. */
{ /* Начинает проверку одного предыдущего диапазона. */
prior_count = word_at(data, prior); /* Предыдущая длина уже подтверждена и не может вывести курсор за массив. */
prior_address = long_at(data, prior + 2UL); /* Читает ранее проверенный Flash-адрес. */
if (address < prior_address + prior_count && prior_address < address + count) return 0x1002U; /* Не разрешает повторную запись слова Flash из разных блоков даже при корректном CRC. */
prior += 6UL + ((Uint32)prior_count << 1); /* Пропускает предыдущий заголовок и все его октеты данных. */
} /* Завершает сравнение с предыдущими секциями. */
offset += bytes; /* Переходит к следующему полностью проверенному блоку. */
} /* Завершает обход блоков. */
return 0x1002U; /* Отсутствие нулевого терминатора делает образ неполным. */
} /* Завершает проверку адресов и структуры. */
#pragma CODE_SECTION(program_image, "flash_rs") /* Весь участок от стирания до завершения выполняется из внутренней RAM. */
static void program_image(RS_DATA *rs, const Uint16 *data, Uint32 length) /* Никогда не возвращается в удалённую программу после первого вызова Flash_Erase. */
{ /* Начинает автономный алгоритм записи и проверки. */
Uint32 offset = 22UL, address, boot_offset = 0UL; /* Хранит курсоры данных и отложенного загрузочного перехода во внутренней RAM. */
Uint16 count, chunk, i, status; /* Состояние API и размеры блоков не зависят от внешних таблиц. */
status = Flash_Erase(0x00FFU, &api_status); /* Маска TI содержит биты A–H; обновление заменяет весь исполняемый образ, параметры внешней EEPROM сохраняются. */
while (!status && offset < length) /* Программирует блоки только после успешного стирания всех секторов. */
{ /* Начинает обработку очередной секции изображения. */
count = word_at(data, offset); /* Использует размер, уже проверенный до входа в критический участок. */
offset += 2UL; /* Пропускает поле количества слов. */
if (!count) break; /* Нулевой блок означает конец файла TI. */
address = long_at(data, offset); /* Восстанавливает Flash-адрес текущей секции. */
offset += 4UL; /* Переводит курсор к полезным словам секции. */
if (address == 0x33FFF6UL) { boot_offset = offset; offset += 4UL; continue; } /* Откладывает boot-переход до успешной записи и проверки остальных секций. */
while (count && !status) /* Делит большие секции на буферные порции без переполнения RAM. */
{ /* Начинает запись очередной порции. */
chunk = count > 128U ? 128U : count; /* Ограничение совпадает с размером внутреннего программирующего буфера в словах. */
for (i = 0; i < chunk; ++i) program_buffer[i] = word_at(data, offset + ((Uint32)i << 1)); /* Распаковывает байты RS в слова, ожидаемые Flash API. */
status = Flash_Program((Uint16 *)address, program_buffer, chunk, &api_status); /* Использует официальный алгоритм записи F28335 вместо обычной записи указателем. */
if (!status) status = Flash_Verify((Uint16 *)address, program_buffer, chunk, &api_status); /* Подтверждает записанные слова чтением из Flash до перехода к следующей порции. */
offset += (Uint32)chunk << 1; /* Продвигает курсор файла на фактически обработанные октеты. */
address += chunk; /* Продвигает Flash-адрес на количество адресуемых слов C28x. */
count -= chunk; /* Уменьшает остаток текущей секции. */
} /* Завершает запись секции. */
} /* Завершает запись обычных блоков. */
if (!status && boot_offset) /* Разрешает запуск нового образа только после проверки всех предыдущих секций. */
{ /* Начинает фиксацию аппаратной точки входа. */
program_buffer[0] = word_at(data, boot_offset); /* Восстанавливает первое слово инструкции длинного перехода. */
program_buffer[1] = word_at(data, boot_offset + 2UL); /* Восстанавливает второе слово адреса перехода. */
status = Flash_Program((Uint16 *)0x33FFF6UL, program_buffer, 2UL, &api_status); /* Записывает boot-переход последним, исключая запуск неполного образа. */
if (!status) status = Flash_Verify((Uint16 *)0x33FFF6UL, program_buffer, 2UL, &api_status); /* Проверяет обе части аппаратной точки входа. */
} /* Завершает фиксацию загрузочного перехода. */
reply_status(rs, status); /* Отправляет конкретный результат после записи и аппаратной проверки. */
if (!status) { EALLOW; SysCtrlRegs.WDCR = 0x0028U; EDIS; } /* Успех включает watchdog для аппаратного перезапуска; режим загрузки задают GPIO84–87. */
for (;;) {} /* Не возвращается в удалённый код; при успехе watchdog перезапустит устройство, при ошибке требуется восстановление. */
} /* Завершает автономный алгоритм. */
void FlashRs_Command(RS_DATA *rs) /* До стирания проверяет исходный блок и копирует все необходимые функции во внутреннюю RAM. */
{ /* Начинает обработку команды действующего загрузчика. */
Uint32 source, length, destination, i; /* Адреса и длина команды передаются младшим октетом вперёд в BIOS-протоколе. */
Uint16 status = 0U, crc, saved; /* Сохраняет код API, CRC и прежнюю маску IRQ для безопасного возврата после пробы либо отказа до стирания. */
source = (Uint32)rs->RS_Header[6] | ((Uint32)rs->RS_Header[7] << 8) | ((Uint32)rs->RS_Header[8] << 16) | ((Uint32)rs->RS_Header[9] << 24); /* Поле источника задаёт адрес ранее загруженного блока RAM. */
length = (Uint32)rs->RS_Header[10] | ((Uint32)rs->RS_Header[11] << 8) | ((Uint32)rs->RS_Header[12] << 16) | ((Uint32)rs->RS_Header[13] << 24); /* Поле длины выражено в октетах файла, как команда INITLOAD. */
destination = (Uint32)rs->RS_Header[2] | ((Uint32)rs->RS_Header[3] << 8) | ((Uint32)rs->RS_Header[4] << 16) | ((Uint32)rs->RS_Header[5] << 24); /* Адрес 0x300000 подтверждает назначение команды внутренней Flash. */
memcpy(&FlashRsRunStart, &FlashRsLoadStart, &FlashRsLoadEnd - &FlashRsLoadStart); /* Копирует функции, константы и библиотеку TI до возможного стирания их загрузочного сектора. */
saved = __disable_interrupts(); /* Не позволяет CAN/таймеру регистратора изменить внешний RAM-буфер между повторной проверкой CRC и передачей управления автономному загрузчику. */
if (CsmRegs.CSMSCR.bit.SECURE) status = 0x1004U; /* Защищённое устройство не должно переходить к стиранию без корректного разблокирования CSM. */
if (!status) status = flash_load_guard(length); /* Активная/неостановленная нагрузка получает отказ до доступа к Flash API; GPIO и уставку меняет только штатный автомат Timer1. */
if (!status && (source || length || destination)) /* Нулевая тройка параметров — только проверка поддержки, без доступа к буферу и стирания. */
{ /* Начинает проверку команды программирования. */
if (destination != 0x300000UL || source < 0x200000UL || source >= 0x20FC00UL || length < 24UL || length > ((0x20FC00UL - source) << 1) - 2UL || !rs->BS_LoadOK || (Uint32)rs->pRecvPtr != source || rs->RS_Length != length + 2UL) status = 0x1003U; /* Принимает лишь CRC-подтверждённый блок в штатной внешней RAM, включая место для принятого CRC. */
if (!status) /* Перепроверяет байты буфера после LOAD, защищая от изменения RAM до стирания. */
{ /* Начинает повторное вычисление CRC исходного кадра LOAD. */
crc = crc_byte(crc_byte(0xFFFFU, rs->addr_recive), CMD_LOAD); /* CRC включает тот же адрес и номер команды, что и первоначальная передача блока. */
for (i = 0; i < length; ++i) crc = crc_byte(crc, byte_at((Uint16 *)source, i)); /* Проверяет каждую полезную октету без таблицы из Flash. */
if (crc != word_at((Uint16 *)source, length)) status = 0x1003U; /* Запрещает стирание, если блок RAM отличается от подтверждённого загрузчиком. */
else status = validate_image((Uint16 *)source, length); /* Проверяет все адреса, завершённость и аппаратный переход до изменения Flash. */
} /* Завершает проверки буфера. */
} /* Завершает предварительную проверку команды. */
if (!status && length) /* Входит в автономный участок только для полностью проверенной ненулевой записи. */
{ /* Начинает остановку обычного приложения перед обновлением. */
Flash_CPUScaleFactor = (Uint32)(1048576.0L * (200.0L * (long double)SYSCLKOUT / 1000000000.0L)); /* Формула TI IQ20 использует реальный SYSCLKOUT в герцах вместо ошибочного фиксированного значения 150 МГц. */
Flash_CallbackPtr = 0; /* Исключает callback из стираемой программы во время операции Flash API. */
DINT; /* Запрещает все обычные ISR, размещённые в обновляемой Flash. */
EALLOW; /* Разрешает запись защищённых регистров watchdog и аварийного отключения PWM. */
SysCtrlRegs.WDCR = 0x0068U; /* Отключает watchdog на время стирания, которое может длиться несколько секунд. */
EPwm1Regs.TZCTL.all = EPwm2Regs.TZCTL.all = EPwm3Regs.TZCTL.all = EPwm4Regs.TZCTL.all = EPwm5Regs.TZCTL.all = EPwm6Regs.TZCTL.all = 0x000AU; /* Коды TZA=2 и TZB=2 переводят оба PWM-выхода в низкий уровень при аварийном отключении. */
EPwm1Regs.TZFRC.bit.OST = EPwm2Regs.TZFRC.bit.OST = EPwm3Regs.TZFRC.bit.OST = EPwm4Regs.TZFRC.bit.OST = EPwm5Regs.TZFRC.bit.OST = EPwm6Regs.TZFRC.bit.OST = 1; /* Принудительно блокирует PWM до перезапуска новой прошивки. */
EDIS; /* Завершает доступ к защищённым регистрам перед вызовом TI API. */
program_image(rs, (Uint16 *)source, length); /* После первого стирания управление остаётся в RAM до ответа и перезапуска. */
} /* Завершает путь программирования. */
reply_status(rs, status); /* Проба поддержки и ошибки до стирания возвращают короткий проверяемый ответ. */
RS_SetBitMode(rs, 9); /* Восстанавливает стандартный приём BIOS после безопасного отказа или пробы возможностей. */
rs->SciRegs->SCICTL2.bit.RXBKINTENA = 1; /* Возвращает обычное обслуживание следующих запросов RS. */
if (rs->commnumber == COM_1) rs->SciRegs->SCIFFRX.bit.RXFFIENA = 1; /* SCI-A в существующем драйвере принимает данные через FIFO-прерывание. */
__restore_interrupts(saved); /* При пробе или отказе до стирания возвращает ровно прежнее состояние прерываний приложения. */
} /* Завершает команду до стирания; после стирания эта функция не получает управление. */
#endif /* Обычный EEPROM/RAM-образ не включает объёмную библиотеку программирования Flash. */
+7 -2
View File
@@ -2,7 +2,7 @@
/* Подключает границы GAS и общий выбор EEPROM; при профиле 0 интерфейс GAS исключён. */
#include "gas167.h"
/* Весь дополнительный код включается только для физически установленной M95M02; вариант AT25256B не растёт. */
#if EEPROM_M95M02
#if EEPROM_M95M02 || defined(FLASH_BOOT) /* Отдельный Flash-вариант включает ту же карту GAS даже при совместимой сборке с AT25256B. */
/* Подключает тип платы dsk_LOAD и псевдонимы регистров, чтобы сохранять правила обычного Modbus. */
#include "package.h"
@@ -18,7 +18,12 @@ static const uint16_t gas167_info[GAS167_INFO_WORDS] = {
/* Слова 0..3: сигнатура GA/S1, версия профиля и номер проекта 167. */
0x4741U, 0x5331U, GAS167_PROFILE_VERSION, 167U,
/* Слова 4..7: база технологических регистров, число слов, маска RTU/CAN/FC06 и признак EEPROM M95M02 = 1; последний элемент описывает аппаратный профиль. */
GAS167_REG_BASE, GAS167_REG_COUNT, GAS167_CAPABILITIES, 1U
GAS167_REG_BASE, GAS167_REG_COUNT, GAS167_CAPABILITIES, /* Первые три поля второй половины дескриптора сохраняют базу, длину и возможности общего GAS 167/v1. */
#if defined(FLASH_BOOT) /* Код исполнения отделяет Flash от загрузки кода из внешней EEPROM без изменения адресов GAS. */
2U /* Значение 2 описывает исполнение из внутренней Flash; физическая EEPROM остаётся заданной EEPROM_M95M02. */
#else /* EEPROM/RAM-вариант M95M02 сохраняет прежний аппаратный признак дескриптора. */
1U /* Значение 1 соответствует прежнему GAS-профилю M95M02 и совместимо с существующими клиентами. */
#endif /* Завершает выбор только последнего слова дескриптора; технологические регистры остаются общими. */
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
};
+19 -69
View File
@@ -1,6 +1,8 @@
/* Порядок запуска периферии и обслуживание команд в главном цикле. Русские пояснения сохранены в UTF-8. */
/* Подключает "DSP2833x_Device.h": поставляемое TI описание Device для семейства C2833x. */
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
#include <string.h> /* Переносит только функции ramfuncs из загрузочного диапазона Flash в рабочую внутреннюю RAM. */
extern unsigned int RamfuncsLoadStart, RamfuncsLoadEnd, RamfuncsRunStart; /* Оба linker-варианта экспортируют границы копирования в адресуемых словах C28x. */
/* Подключает "cntrl_adr.h": собственный и универсальный адрес устройства. */
#include "cntrl_adr.h"
@@ -70,9 +72,9 @@ void main()
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет i — данные i; j — данные j; mask — маска. */
int i,j,mask;
/* Объявляет cancount — данные cancount; cancell — данные cancell; rscount — данные rscount. */
static unsigned int cancount[2], cancell[2], rscount;
int i,j; /* Счётчики обслуживают RS-порты; курсоры CAN и маски теперь принадлежат независимому сервису телеметрии. */
/* Делитель внешнего Modbus-опроса принадлежит main; состояние рассылки CAN хранится и сбрасывается в ecan.c. */
static unsigned int rscount; /* Сохраняет только делитель опроса OWEN; частота CAN больше не зависит от этого счётчика. */
/* Объявляет rs — данные rs. Хранит указатель на данные. */
RS_DATA * rs;
/* Объявляет presbyt — данные presbyt. Начальное значение задаётся справа от знака равенства. */
@@ -80,6 +82,9 @@ void main()
/* Инициализирует модуль вызовом InitSysCtrl(); перед его дальнейшим использованием. */
InitSysCtrl();
if (&RamfuncsLoadStart != &RamfuncsRunStart) /* RAM-образ не копирует одинаковые диапазоны; Flash-образ переносит InitFlash перед изменением wait states. */
memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, &RamfuncsLoadEnd - &RamfuncsLoadStart); /* Размер равен разности адресуемых 16-битных слов TI, а не физическим октетам ПК. */
InitFlash(); /* Вызывает находящийся в RAM код настройки Flash-пайплайна и задержек для штатного SYSCLKOUT. */
// Disable CPU interrupts and clear all CPU interrupt flags:
/* Запрет маскируемых прерываний ядра. */
@@ -148,7 +153,8 @@ void main()
Load_control_init(); /* Explicit initialization before IRQ. */ /* Вызывает Load_control_init. Сбрасывает все переменные автомата нагрузки до разрешения прерываний; COFF не обнуляет всю .ebss. */
/* Вызывает set_cntrl_addr. Устанавливает собственный и универсальный адрес для проверки адресованных команд. */
set_cntrl_addr(Mode,16);
/* Вызывает timer_Init. Назначает обработчик Timer1 по типу платы и задаёт период основного такта. */
rscount = 0U; /* Обнуляет делитель OWEN до Timer1, чтобы остатки RAM не задержали первый опрос внешнего прибора. */
/* Назначает обработчик Timer1 по типу платы и задаёт период основного такта после сброса программных делителей. */
timer_Init();
/* Вызывает EnableInterrupts с аргументами () в текущей последовательности операций. */
EnableInterrupts();
@@ -196,72 +202,16 @@ void main()
/* Чтение логгера по CAN не блокирует цикл управления. */
/* Вызывает UMP_CAN_service. В главном цикле готовит замороженный ответ служебного CAN и отправляет его двухсловными фрагментами. */
UMP_CAN_service();
/* Проверяет if(CanGO); последующий оператор выполняется только при истинном условии. */
if(CanGO)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Записывает CanGO значением 0; CAN GO. */
CanGO=0;
CAN_telemetry_service(); /* Обслуживает heartbeat 1 Гц, изменения и повторы 1,3 с независимо от делителя OWEN и состояния CanGO. */
if (CanGO) { /* Миллисекундный Timer1 по-прежнему выдаёт один флаг каждые CANPOWSE=20 мс для внешнего опроса. */
CanGO = 0; /* Поглощает текущий такт опроса, не ограничивая выдачу уже готовой очереди CAN. */
if (Owncount && ++rscount >= Owncount) { /* Нулевой период отключает OWEN; ненулевой задаёт прежнее число шагов по 20 мс. */
rscount = 0U; /* Начинает очередной интервал после отправки запроса внешнему регулятору. */
SendCommandModbus4(&rs_b); /* Сохраняет добавленный в 167 опрос измерений OWEN по второму UART. */
} /* Заканчивает обслуживание делителя внешнего прибора без блокирующих ожиданий CAN. */
} /* Следующие фоновые операции выполняются при любом состоянии флага периодического опроса. */
/* Проверяет if(Owncount); последующий оператор выполняется только при истинном условии. */
if(Owncount)
/* Проверяет if(++rscount>=Owncount); последующий оператор выполняется только при истинном условии. */
if(++rscount>=Owncount)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Записывает rscount значением 0; данные rscount. */
rscount=0;
/* Вызывает SendCommandModbus4 с аргументами (&rs_b) в текущей последовательности операций. */
SendCommandModbus4(&rs_b);
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Цикл for(i=0;i<2;i++); задаёт начальное значение, условие продолжения и шаг перебора. */
for(i=0;i<2;i++)
/* Проверяет if(++cancount[i] >= Cancount[i]); последующий оператор выполняется только при истинном условии. */
if(++cancount[i] >= Cancount[i])
/* Проверяет if(Cancount[i] и cancount[0]); последующий оператор выполняется только при истинном условии. */
if(Cancount[i] && cancount[0])
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Записывает cancount[i] значением 0; данные cancount. */
cancount[i] = 0;
/* Повторяет или ожидает, пока выполняется while(1). */
while(1)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Проверяет if(cancell[i] >= 0x80) cancell[i]=0; последующий оператор выполняется только при истинном условии. */
if(cancell[i] >= 0x80) cancell[i]=0;
/* Записывает mask значением Maska[i][cancell[i]/16] >> (cancell[i]%16); маска. */
mask = Maska[i][cancell[i]/16] >> (cancell[i]%16);
/* Проверяет if(!mask) cancell[i] = (cancell[i] + 0x10) & 0xFFF0; последующий оператор выполняется только при истинном условии. */
if(!mask) cancell[i] = (cancell[i] + 0x10) & 0xFFF0;
/* Выполняет альтернативу, если предыдущее условие if оказалось ложным. */
else
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Повторяет или ожидает, пока выполняется while(!(mask & 1)). */
while(!(mask & 1))
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Изменяет счётчик в выражении cancell[i]++; mask >>= 1; на один для следующего шага. */
cancell[i]++; mask >>= 1;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Прерывает ближайший цикл или ветвь switch и продолжает после него. */
break;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
} }
/* Вызывает CAN_send. Упаковывает адрес и технологические слова в штатный CAN-кадр и запускает передачу mailbox. */
CAN_send(0,modbus,cancell[i]);
/* Увеличивает cancell[i] значением 3; данные cancell. */
cancell[i]+=3;
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
} }
/* Проверяет if(cDefParam); последующий оператор выполняется только при истинном условии. */
if(cDefParam)
if(cDefParam) /* Применяет заводские параметры по команде, сохраняя действующие биты управления нагрузкой в обработчике параметров. */
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Записывает cDefParam значением 0; данные cDefParam. */
+2 -2
View File
@@ -698,8 +698,8 @@ void Init_packMask()
{
/* Ветвь выбора для dsk_EPLT; выполняется при совпадении селектора switch. */
case dsk_EPLT:
/* Устанавливает биты Maska[m_SLOW][0] значением 0x000F; данные Maska. */
Maska[m_SLOW][0]|= 0x000F; // Полученное
/* Маска 0x000F выбирает R0–R3 пульта для быстрой передачи аварийных и управляющих флагов. */
Maska[m_FAST][0]|= 0x000F; /* Возвращает период передачи этих четырёх слов из ветки 166, не добавляя их в медленный поток. */
/* Устанавливает биты Maska[m_SLOW][6] значением 0x01FC; данные Maska. */
Maska[m_SLOW][6]|= 0x01FC; // Яркость ламп и одиночество
/* Прерывает ближайший цикл или ветвь switch и продолжает после него. */
+26 -12
View File
@@ -59,12 +59,21 @@ 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()
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет i — данные i; def — данные def. */
unsigned int i,def;
/* Объявляет индекс параметров, номер заводской калибровки и сохранённую маску IRQ. */
unsigned int i,def,saved; /* Хранит индекс, номер заводской калибровки и прежнюю маску IRQ для согласованного применения параметров. */
/* Объявляет shkf_lo_edge — данные shkf_lo_edge. Начальное значение задаётся справа от знака равенства. */
int shkf_lo_edge[] = {300, // 380
@@ -88,12 +97,13 @@ void Default_params()
/* Объявляет 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; представление регистра целым словом без разбиения на битовые поля. */
Modbus[i].all = 0;
if (Desk != dsk_LOAD || i != 127U) Modbus[i].all = 0; /* Слово R127 нагрузки содержит живые команды; его очистка создала бы ложный STOP и повторный START. */
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
@@ -263,6 +273,9 @@ void Default_params()
/* Записывает 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();
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
@@ -272,8 +285,8 @@ void Default_params()
void Load_params()
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Объявляет i — данные i; crc — данные crc. */
unsigned int i,crc;
/* Объявляет индекс параметров, контрольную сумму и сохранённую маску 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);
@@ -287,10 +300,12 @@ void Load_params()
(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;
/* Чтение EEPROM уже завершено: короткая секция ниже согласованно применяет настройки защиты из буфера param. */
saved = __disable_interrupts(); /* Применяет лимит попыток и тайм-аут единым снимком; длительное чтение EEPROM выше остаётся доступным для IRQ. */
for(i=0;i<ANSWER_LEN;i++) /* Переносит проверенную карту параметров, не выдавая аппаратных сигналов и не сбрасывая автомат167. */
if (Desk != dsk_LOAD || i != 127U) modbus[i] = param[i]; /* Никогда не копирует сохранённый START/RESET нагрузки, даже на один промежуточный такт Timer1. */
reset_parameter_commands(); /* Сохраняет последние принятые START/RESET после чтения EEPROM; до READY очищает команды полностью. */
__restore_interrupts(saved); /* Возвращает исходное состояние IRQ после согласованного применения RAM-настроек. */
/* Закрывает текущий блок или список полей; следующие операторы относятся к внешнему уровню. */
}
/* Выполняет альтернативу, если предыдущее условие if оказалось ложным. */
@@ -299,8 +314,7 @@ void Load_params()
{
/* Восстанавливает заводские параметры после неверного CRC или несоответствия режима платы; LOAD_CAN_TIMEOUT получает 2000 мс. */
Default_params();
/* Записывает Commands значением 0; слово команд, технологический регистр 127. */
Commands=0;
/* Default_params уже сохранила текущие управляющие уровни нагрузки; повторная очистка R127 здесь недопустима. */
/* Вызывает Save_params. Сохраняет технологические регистры в область параметров, отделённую от загрузочного образа EEPROM. */
Save_params();
/* Проверяет if(TermoSW); последующий оператор выполняется только при истинном условии. */
@@ -437,7 +451,7 @@ static int receive_ump(RS_DATA *rs_arr)
/* Записывает saved значением __disable_interrupts(); данные saved. */
saved = __disable_interrupts();
/* Условие компиляции EEPROM_M95M02: выбранный блок включается в текущий вариант прошивки. */
#if 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); число. */
+1 -2
View File
@@ -405,8 +405,7 @@ void get_Buttons()
if(Desk==dsk_EPLT)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */
{
/* Сохраняет предыдущее состояние TESTLAMP, пока накопитель INcount0 не достигнет одного из порогов; 1000 — число опросов главного цикла, а не миллисекунды. Краткий провал GPIO26 больше не переключает передачу с тестовых масок на рабочие. */
bTestLamp = er_anal(GpioDataRegs.GPADAT.bit.GPIO26, &INcount0, 1000, bTestLamp);
/* TESTLAMP обрабатывается Timer1 в pulto.c: опрос остальных кнопок не должен менять отсчитанную аппаратным таймером задержку. */
/* Проверяет if(GpioDataRegs.GPADAT.bit.GPIO27) butt +=0x0001; последующий оператор выполняется только при истинном условии. */
if(GpioDataRegs.GPADAT.bit.GPIO27) butt +=0x0001;
+10
View File
@@ -70,6 +70,8 @@ interrupt void cpu_timer1_isr_PULT(void)
unsigned int light=0, i;
/* Объявляет Alone — данные Alone; READY_FREQ — частота основного такта Timer1 в герцах. Хранит указатель на данные. Начальное значение задаётся справа от знака равенства. */
unsigned int Alone = 20*READY_FREQ;
/* Считает непрерывное удержание TESTLAMP в тактах Timer1: READY_FREQ задаёт число тактов за секунду, независимо от занятости main. */
static unsigned int test_lamp_hold_ticks = 0;
/* Разрешение записи в защищённые регистры периферии. */
EALLOW;
@@ -121,6 +123,14 @@ interrupt void cpu_timer1_isr_PULT(void)
/* Проверяет if(++cownt>2) cownt=0; последующий оператор выполняется только при истинном условии. */
if(++cownt>2) cownt=0;
if(GpioDataRegs.GPADAT.bit.GPIO26) /* Высокий уровень входа означает нажатую физическую кнопку TESTLAMP на плате пульта. */
{ /* Накапливает только непрерывное удержание; насыщение защищает счётчик от переполнения при долгом нажатии. */
if(test_lamp_hold_ticks < 2U * (unsigned int)READY_FREQ) /* Порог двух секунд перенесён из 166: короткое нажатие не включает общий тест индикации. */
test_lamp_hold_ticks++; /* Добавляет один аппаратный такт, поэтому задержка не зависит от обмена и чтения EEPROM в главном цикле. */
} /* Завершает накопление текущего удержания физической кнопки. */
else test_lamp_hold_ticks = 0; /* Отпускание сразу отменяет тест и не позволяет суммировать отдельные короткие нажатия. */
bTestLamp = (test_lamp_hold_ticks >= 2U * (unsigned int)READY_FREQ); /* Обновляет флаг до отправки очередного пакета; удалённая команда cTestLamp сохраняет отдельное управление. */
/* Проверяет if(cTestLamp|bTestLamp); последующий оператор выполняется только при истинном условии. */
if(cTestLamp|bTestLamp)
/* Открывает составной блок или описание полей; следующие строки принадлежат этому уровню вложенности. */