Добавить общее ядро DEIF, эмуляцию приборов и переносимые панели Python/Qt. Расширить загрузчики F2812 и F28335, обновление Flash и профиль STM Invertor. Добавить альтернативный МНК Eigen, модели неисправностей и диагностику сигналов. Обновить справочники устройств, проекты CCS и регрессионные проверки.
333 lines
30 KiB
C
333 lines
30 KiB
C
/* Separate Flash recovery loader and application service adapter. */
|
||
#include "DSP2833x_Device.h"
|
||
#include "f28335_can_boot.h"
|
||
#if EEPROM_M95M02
|
||
#include "f28335_boot_platform.h"
|
||
#include <string.h>
|
||
/* boot хранит текущую CAN-сессию в RAM, store — прочитанное состояние журнала.
|
||
* Сброс сессии не удаляет active/pending из EEPROM. */
|
||
static pcan_boot_t boot;
|
||
static f28335_boot_store_t store;
|
||
static unsigned int healthy; /* Результат открытия EEPROM-журнала известен также функции выбора назначения Flash. */
|
||
#if F28335_BOOT_LOADER && F28335_CAN_FLASH_UPDATE /* Flash API доступен только постоянному загрузчику с явной поддержкой обновления B–H. */
|
||
#include "f28335_flash_update.h" /* Подключает проверку Flash SCI8 и аппаратный writer с сохранением сектора A. */
|
||
#include "f28335_flash_update.c" /* Общий алгоритм компилируется с тем же pcan_boot и разметкой, что этот порт. */
|
||
static f28335_flash_update_t flash_update; /* Индекс и кеш занимают ограниченную внутреннюю RAM без загрузки приложения поверх boot. */
|
||
static unsigned int flash_selected, flash_staging; /* Явная цель Flash использует только неактивный EEPROM-слот как временное хранилище. */
|
||
static pcan_boot_status_t select_memory(void *u, pcan_boot_byte_t memory, pcan_boot_config_t *cfg) /* Выбирает назначение до BEGIN_IMAGE, сохраняя прежний EEPROM-путь по умолчанию. */
|
||
{ /* Изменения конфигурации разрешены только при исправном EEPROM-журнале. */
|
||
(void)u; if (!healthy) return PCAN_BOOT_STATUS_FLASH_ERROR; /* Ошибка чтения журнала запрещает выбор staging поверх неизвестного active. */
|
||
if (memory > 1U) return PCAN_BOOT_STATUS_INVALID_COMMAND; /* Единственные определённые цели: 0=EEPROM A/B и 1=внутренняя Flash. */
|
||
flash_selected = memory; flash_update.valid = false; /* Новая цель отменяет прежнее разрешение программирования. */
|
||
cfg->slot_count = memory ? 1U : 2U; cfg->active_slot = memory ? 0U : store.active; /* CAN Flash использует один логический слот A, EEPROM сохраняет выбор неактивного A/B. */
|
||
return PCAN_BOOT_STATUS_OK; /* Физический staging будет инвалидирован лишь при последующей команде ERASE. */
|
||
} /* Никакие данные Flash или EEPROM при выборе цели не изменяются. */
|
||
#endif /* Приложение и старые host-модели не подключают Flash API. */
|
||
/* Кольцевая очередь: ISR производит кадры, main потребляет. Из 32 элементов
|
||
* используется 31, чтобы head==tail однозначно означало пустую очередь.
|
||
* Долгие операции SPI запрещены внутри обработчика CAN. */
|
||
static volatile unsigned int head,tail,overflow;
|
||
static struct { uint32_t id; unsigned int dlc; pcan_boot_byte_t data[8]; } queue[32];
|
||
/* reset_requested только планирует reset; Service ждёт завершения TX.
|
||
* ready сообщает об окончании старта приложения, confirmed — о сохранённом
|
||
* подтверждении слота. healthy отражает результат открытия журнала при Init. */
|
||
static unsigned int reset_requested,reset_session,ready,confirmed; /* Состояние перехода и подтверждения приложения отделено от доступности EEPROM. */
|
||
static uint32_t sent_at,ready_at;
|
||
static unsigned int main_passes;
|
||
static unsigned int addressed; /* Разрешает обслуживание только после проверки номера PERIPH и установки согласованных фильтров CAN. */
|
||
static int tx_busy,tx_canceling;
|
||
/* Один исходящий mailbox исключает перезапись ещё передаваемого ответа.
|
||
* MDL содержит первые четыре октета, MDH — следующие четыре, младшим вперёд.
|
||
* Запрос CANTRS ещё не означает успешную доставку: CANTA проверяется в Service.
|
||
* Аппаратный CAN ACK не является отдельным прикладным подтверждением от ПК. */
|
||
static bool transmit(void *u, uint32_t id, const pcan_boot_byte_t *d, pcan_boot_byte_t n)
|
||
{
|
||
unsigned int i; unsigned long lo=0, hi=0; (void)u;
|
||
if (tx_busy) return false;
|
||
for(i=0;i<n;++i) {
|
||
if(i<4) lo|=(unsigned long)(d[i]&255U)<<(8U*i);
|
||
else hi|=(unsigned long)(d[i]&255U)<<(8U*(i-4U));
|
||
}
|
||
EALLOW;
|
||
ECanaRegs.CANME.all &= ~16UL;
|
||
ECanaMboxes.MBOX4.MSGID.all=0x80000000UL|id;
|
||
ECanaMboxes.MBOX4.MSGCTRL.all=n;
|
||
ECanaMboxes.MBOX4.MDL.all=lo; ECanaMboxes.MBOX4.MDH.all=hi;
|
||
ECanaRegs.CANME.all |=16UL;
|
||
ECanaRegs.CANTA.all=16UL; ECanaRegs.CANAA.all=16UL;
|
||
ECanaRegs.CANTRS.all=16UL;
|
||
EDIS;
|
||
sent_at=F28335Boot_Now(); tx_busy=1; tx_canceling=0; return true;
|
||
}
|
||
|
||
/* Изменение образа разрешено только сборке Flash-загрузчика. В приложении
|
||
* эти callbacks возвращают отказ, даже если им удастся передать команду записи.
|
||
* ERASE для EEPROM означает инвалидирование manifest выбранного слота. */
|
||
static bool erase(void *u,pcan_boot_byte_t slot)
|
||
{ /* ERASE подготавливает staging; внутренняя Flash остаётся неизменной до VERIFY и COMMIT. */
|
||
#if F28335_BOOT_LOADER && F28335_CAN_FLASH_UPDATE /* Новая цель не меняет прежний формат EEPROM A/B. */
|
||
if (flash_selected) { flash_update.valid = false; flash_staging = store.active == 0U ? 1U : 0U; return healthy && f28335_boot_prepare(&store, flash_staging); } /* Инвалидирует только неактивный слот, сохраняя подтверждённый резерв и параметры. */
|
||
#endif /* В остальных сборках остаётся штатная подготовка EEPROM-слота. */
|
||
(void)u; return F28335_BOOT_LOADER && healthy && f28335_boot_prepare(&store,slot); /* Обычный CAN-клиент сохраняет прежний путь обновления RAM-приложения. */
|
||
} /* Подготовка не вызывает TI Flash_Erase. */
|
||
static bool write_block(void *u,pcan_boot_byte_t slot,uint32_t offset,const pcan_boot_byte_t *d,pcan_boot_byte_t n)
|
||
{ /* CAN ISR только ставит данные в очередь; запись staging выполняет main. */
|
||
#if F28335_BOOT_LOADER && F28335_CAN_FLASH_UPDATE /* Запись Flash-файла использует тот же проверенный EEPROM writer. */
|
||
if (flash_selected) return healthy && f28335_boot_write(&store, flash_staging, offset, d, n); /* Логический DATA_A направляется в выбранный неактивный физический слот. */
|
||
#endif /* Обычные A/B-данные сохраняют исходную адресацию. */
|
||
(void)u; return F28335_BOOT_LOADER && healthy && f28335_boot_write(&store,slot,offset,d,n); /* Не допускает записи из прикладной сборки. */
|
||
} /* Внутренняя Flash ещё не затронута. */
|
||
/* Проверка включает CRC EEPROM и структуру SCI8. Сейчас отказ этого слоя
|
||
* кодируется CRC_ERROR как общий отказ проверки образа; причины внутри
|
||
* парсера SCI8 отдельными статусами протокола не различаются. */
|
||
static pcan_boot_status_t verify(void *u,pcan_boot_byte_t slot,const pcan_boot_manifest_t *m)
|
||
{
|
||
(void)u;
|
||
if(!F28335_BOOT_LOADER || !healthy) return PCAN_BOOT_STATUS_INVALID_STATE;
|
||
#if F28335_BOOT_LOADER && F28335_CAN_FLASH_UPDATE /* Для внутренней Flash нужен собственный допустимый диапазон SCI8 и обязательный FAB1. */
|
||
if (flash_selected) return f28335_flash_update_validate(&flash_update, &store.io, flash_staging ? F28335_AB_SLOT_B : F28335_AB_SLOT_A, m) ? PCAN_BOOT_STATUS_OK : PCAN_BOOT_STATUS_CRC_ERROR; /* Повторно читает EEPROM и проверяет транспортную CRC, таблицу и CRC будущей Flash до erase. */
|
||
#endif /* RAM SCI8 проходит прежнюю проверку приложения EEPROM. */
|
||
return f28335_boot_verify(&store,slot,m,0)?PCAN_BOOT_STATUS_OK:PCAN_BOOT_STATUS_CRC_ERROR;
|
||
}
|
||
static bool commit(void *u,pcan_boot_byte_t slot,const pcan_boot_manifest_t *m)
|
||
{ /* COMMIT выполняет фактическую запись только после подтверждённой проверки образа. */
|
||
#if F28335_BOOT_LOADER && F28335_CAN_FLASH_UPDATE /* Постоянный boot остаётся вне обновляемых секторов B–H. */
|
||
if (flash_selected) { /* Синхронный TI API может работать дольше обычного интервала CAN, не создавая pending EEPROM. */
|
||
bool result = healthy && F28335Boot_FlashProgram(&flash_update); /* Итог зависит от erase, каждой порции readback и полной CRC физической Flash. */
|
||
boot.now_ms = F28335Boot_Now(); boot.last_activity_ms = boot.now_ms; return result; /* Обновляет срок сессии после аппаратной операции, чтобы штатный REBOOT не был отклонён из-за времени записи. */
|
||
} /* Ошибка записи оставляет постоянный boot доступным для нового сеанса. */
|
||
#endif /* Обычное обновление EEPROM сохраняет trial/CONFIRM. */
|
||
(void)u; return F28335_BOOT_LOADER && healthy && f28335_boot_commit(&store,slot,m); /* Публикует только RAM-образ в исходном журнале A/B. */
|
||
} /* Итоговый статус и отдельный REBOOT отправляет существующее ядро ProtoCAN. */
|
||
/* Подтверждение принадлежит приложению после READY. Загрузчик не может
|
||
* подтвердить кандидата сам: иначе исчезнет смысл проверки пробного запуска.
|
||
* Разрешение кормления watchdog из ISR открываем только после записи журнала. */
|
||
static bool confirm(void *u)
|
||
{
|
||
unsigned int running=F28335Boot_RunningSlot(); (void)u;
|
||
if(F28335_BOOT_LOADER || !healthy || !ready || running>1) return false;
|
||
if(!f28335_boot_confirm(&store,running)) return false;
|
||
boot.config.active_slot=running; confirmed=1; return true;
|
||
}
|
||
static bool can_reboot(void *u) { (void)u; return healthy; }
|
||
static void reboot(void *u) { (void)u; reset_session=0; reset_requested=1; }
|
||
/* Контракт enter_boot: 0 — загрузчик готов и ядро может отправить ACK;
|
||
* 1 — переход отложен, ACK придёт после reset; 2 — вход запрещён.
|
||
* Приложение требует handoff нашего загрузчика и подтверждённый текущий слот,
|
||
* чтобы не начать обновление из старого SPI-ROM старта или незавершённой пробы. */
|
||
static unsigned int enter(void *u,pcan_boot_byte_t session)
|
||
{
|
||
(void)u;
|
||
if(!healthy) return 2;
|
||
if(F28335_BOOT_LOADER) { /* Постоянный загрузчик открывает сессию без дополнительного reset. */
|
||
#if F28335_CAN_FLASH_UPDATE /* Каждая новая сессия должна начинаться со штатного EEPROM-назначения. */
|
||
if (boot.state == PCAN_BOOT_STATE_IDLE) (void)select_memory(0, 0U, &boot.config); /* Повторный ENTER текущей сессии не меняет уже выбранную Flash. */
|
||
#endif /* Старые сборки не используют переключение назначения. */
|
||
return 0; /* Ядро отправит адресный ACK готовности. */
|
||
} /* Прикладной handoff проверяется ниже. */
|
||
/* No destructive fallback on boards still starting through the SPI ROM. */
|
||
if((F28335Boot_RunningSlot()>1 && F28335Boot_RunningSlot()!=F28335_BOOT_FLASH_SLOT) || !confirmed) return 2; /* Только handoff общего загрузчика разрешает переход из EEPROM либо защищённой внутренней Flash; старый SPI-ROM старт остаётся запрещён. */
|
||
reset_session=session; reset_requested=1; return 1;
|
||
}
|
||
static void info32(pcan_boot_byte_t *p,uint32_t v)
|
||
{ unsigned int i; for(i=0;i<4;++i) p[i]=(pcan_boot_byte_t)((v>>(i*8))&255U); }
|
||
/* Паспорт помещается в classic CAN по страницам из восьми октетов.
|
||
* NAME возвращает фрагменты строки с завершающим NUL; неизвестная страница
|
||
* отклоняется. MEMORY: 0 — ёмкость/страница/режим/running, 1 — базы A/B,
|
||
* 2 — active/pending/attempted/healthy/тайм-аут, 3 — область параметров. */
|
||
static bool info(void *u,pcan_boot_byte_t command,pcan_boot_byte_t page,pcan_boot_byte_t *out)
|
||
{
|
||
const char *name=boot.config.device_type==1U && boot.config.device_id==8U?"Zadatchik":boot.config.device_type==1U && boot.config.device_id==5U?"UMP1":boot.config.device_type==1U && boot.config.device_id==6U?"UMP2":F28335_CAN_BOOT_DEVICE_NAME; /* Паспорт различает три назначенных Excel устройства; резервные номера сохраняют аппаратное имя. */
|
||
unsigned int i,index,name_size=(unsigned int)strlen(name)+1U; (void)u; /* Длина включает завершающий NUL, чтобы последняя страница имени однозначно завершала чтение. */
|
||
if(command==PCAN_BOOT_CMD_CAPABILITIES && page==0) {
|
||
/* bits: ping, discovery, identity, name, memory, A/B, confirm, rollback,
|
||
* separate loader, session timeout, readback, SCI8 validation. */
|
||
info32(out,(F28335_BOOT_LOADER || F28335Boot_RunningSlot()<2 || F28335Boot_RunningSlot()==F28335_BOOT_FLASH_SLOT)?0xFFFUL:0x1FUL); /* Flash-приложение с достоверным handoff поддерживает вход в постоянный CAN Boot так же, как подтверждённый EEPROM-образ. */
|
||
#if F28335_BOOT_LOADER && F28335_CAN_FLASH_UPDATE /* Дополнительный бит сообщает клиенту поддержку явной внутренней Flash. */
|
||
out[1] |= 0x10U; /* Бит 12 общего little-endian capability word означает SELECT_MEMORY и запись B–H. */
|
||
#endif /* Старые приложения продолжают сообщать прежние возможности. */
|
||
info32(out+4,F28335_AB_SLOT_BYTES); return true; /* Максимальная длина одинакова для staging Flash и EEPROM A/B. */
|
||
}
|
||
if(command==PCAN_BOOT_CMD_DEVICE_NAME) {
|
||
if((unsigned int)page*8U>=name_size) return false; /* Страница после конца выбранного имени не является допустимой частью паспорта. */
|
||
for(i=0;i<8;++i) { index=(unsigned int)page*8U+i;
|
||
out[i]=index<name_size?(pcan_boot_byte_t)name[index]:0; } /* Дополняет последнюю восьмибайтовую страницу нулями без выхода за строку. */
|
||
return true;
|
||
}
|
||
if(command!=PCAN_BOOT_CMD_MEMORY_INFO) return false;
|
||
if(page==0) { info32(out,0x40000UL); out[4]=0; out[5]=1;
|
||
out[6]=F28335_BOOT_LOADER?1:0; out[7]=F28335Boot_RunningSlot(); return true; }
|
||
if(page==1) { info32(out,F28335_AB_SLOT_A); info32(out+4,F28335_AB_SLOT_B); return true; }
|
||
if(page==2) { out[0]=store.active; out[1]=store.pending; out[2]=store.attempted;
|
||
out[3]=healthy?1:0; info32(out+4,boot.config.session_timeout_ms); return true; }
|
||
if(page==3) { info32(out,F28335_AB_PARAMS); info32(out+4,258); return true; }
|
||
return false;
|
||
}
|
||
/* Вызывается до разрешения прикладных IRQ. Нельзя полагаться на случайное
|
||
* содержимое RAM или оставшееся состояние confirmed от предыдущей программы. */
|
||
void F28335CanBoot_EarlyInit(void) { confirmed=0; ready=0; }
|
||
/* Общий Init для приложения и загрузчика: открываем журнал, привязываем
|
||
* callbacks, задаём идентификацию, ёмкость слота, окно 16 и тайм-аут 30 секунд.
|
||
* В загрузчике FW берётся из active manifest; приложение сообщает версию сборки. */
|
||
void F28335CanBoot_Init(unsigned int periph_id) /* Один номер платы преобразуется одинаково в приложении и постоянном загрузчике. */
|
||
{
|
||
pcan_boot_config_t cfg; pcan_boot_port_t port;
|
||
f28335_boot_io_t io; unsigned int running=F28335Boot_RunningSlot();
|
||
addressed=0U; EALLOW; ECanaRegs.CANME.all&=~88UL; EDIS; /* Закрывает только Boot mailbox 3/4/6 до проверки адреса, включая повторный Init с ошибочным номером. */
|
||
if(periph_id>=F28335_CAN_BOOT_PERIPH_COUNT) return; /* Номер вне Excel-диапазона 0–31 не подменяется общим адресом и не открывает запись другой платы. */
|
||
memset(&cfg,0,sizeof(cfg)); memset(&port,0,sizeof(port));
|
||
io.read=F28335Boot_Read; io.write=F28335Boot_Write; io.user=0;
|
||
healthy=f28335_boot_open(&store,&io);
|
||
cfg.device_type=1U+periph_id/16U; cfg.device_id=periph_id%16U; /* Excel: PERIPH 0–15 → Type 1, 16–31 → Type 2; младшие четыре бита задают Device ID. */
|
||
cfg.product_type=F28335_CAN_BOOT_PRODUCT; cfg.hardware_revision=1;
|
||
#if F28335_BOOT_LOADER
|
||
cfg.firmware_version=store.active<2?store.image[store.active].firmware_version:0;
|
||
#else
|
||
cfg.firmware_version=F28335_CAN_BOOT_FIRMWARE_VERSION;
|
||
#endif
|
||
head=tail=overflow=reset_requested=reset_session=ready=main_passes=0; tx_busy=tx_canceling=0;
|
||
confirmed=running==F28335_BOOT_FLASH_SLOT || (healthy && running<2 && store.active==running && store.pending==F28335_AB_NONE); /* Flash уже проверена CRC постоянным boot; пробное подтверждение требуется только EEPROM-кандидату. */
|
||
cfg.active_slot=store.active;
|
||
cfg.slot_count=2; cfg.max_image_size=F28335_AB_SLOT_BYTES; cfg.ack_window=16;
|
||
cfg.session_timeout_ms=30000UL;
|
||
#if F28335_BOOT_LOADER && F28335_CAN_FLASH_UPDATE /* После reset никакое случайное содержимое RAM не выбирает внутреннюю Flash. */
|
||
flash_selected = 0U; flash_staging = F28335_AB_NONE; flash_update.valid = false; /* Возвращает исходную цель и отменяет разрешение записи предыдущей сессии. */
|
||
port.select_memory = select_memory; /* Новая команда доступна только сборке с реально подключённым TI API. */
|
||
#endif /* Приложение отвечает только через постоянный загрузчик после handoff. */
|
||
port.send=transmit; port.erase_slot=erase; port.write_slot=write_block;
|
||
port.verify_status=verify; port.set_pending_slot=commit; port.reboot=reboot;
|
||
port.confirm_running_slot=confirm; port.enter_boot=enter;
|
||
port.can_reboot=can_reboot; port.service_info=info;
|
||
if(!pcan_boot_init(&boot,&cfg,&port,0)) return;
|
||
addressed=1U; /* Дальнейшие ISR, основной цикл и handoff используют только успешно созданную адресную конфигурацию. */
|
||
EALLOW;
|
||
/* Меняем только MBOX3/4/6: маска 88=8+16+64. MBOX0..2 — старый обмен,
|
||
* MBOX5 — heartbeat. Приём broadcast выделен в отдельный точный mailbox,
|
||
* чтобы слишком широкая маска Boot не перехватывала сообщения приложения.
|
||
* 72=8+64 — оба RX, 16 — TX. Регистры флагов очищаются записью единицы. */
|
||
ECanaRegs.CANME.all &= ~88UL;
|
||
ECanaMboxes.MBOX3.MSGID.all=0xC0000000UL|pcan_boot_make_id(0,0,cfg.device_type,cfg.device_id,0,0);
|
||
ECanaLAMRegs.LAM3.all=0x100FFFFFUL;
|
||
ECanaMboxes.MBOX3.MSGCTRL.all=8;
|
||
/* MBOX5 belongs to the application's heartbeat. MBOX6 is discovery only. */
|
||
ECanaMboxes.MBOX6.MSGID.all=0xC0000000UL|pcan_boot_make_id(0,0,7,15,13,0);
|
||
ECanaLAMRegs.LAM6.all=0x10000000UL; /* Ignore priority only. */
|
||
ECanaMboxes.MBOX6.MSGCTRL.all=0;
|
||
ECanaRegs.CANMD.all=(ECanaRegs.CANMD.all|72UL)&~16UL;
|
||
ECanaRegs.CANRMP.all=72UL;
|
||
ECanaRegs.CANMIM.all |=72UL;
|
||
ECanaRegs.CANME.all |=88UL;
|
||
EDIS;
|
||
}
|
||
/* ISR только проверяет заголовок и копирует кадр. Чужие адреса и RTR
|
||
* отбрасываются до очереди. Глобальный адрес допускается только для discovery;
|
||
* DLC и смысл команды окончательно проверит ядро в основном цикле. */
|
||
static void receive_box(volatile struct MBOX *box,uint32_t mask)
|
||
{
|
||
unsigned int next=(head+1U)&31U, i;
|
||
unsigned long lo, hi;
|
||
uint32_t id; unsigned int type;
|
||
if(!(ECanaRegs.CANRMP.all&mask)) return;
|
||
if(box->MSGCTRL.bit.RTR) { ECanaRegs.CANRMP.all=mask; return; }
|
||
id=box->MSGID.all&0x1FFFFFFFUL;
|
||
type=pcan_boot_id_msg_type(id);
|
||
if(!(box->MSGID.all&0x80000000UL) || pcan_boot_id_route(id)!=0 ||
|
||
(type!=9 && type!=10 && type!=11 && type!=13) ||
|
||
!((pcan_boot_id_device_type(id)==boot.config.device_type && /* Сравнивает тип именно этой платы, а не прежнюю общую константу 3. */
|
||
pcan_boot_id_device_id(id)==boot.config.device_id) || /* Отбрасывает команды соседних PERIPH ещё до очереди и любых операций EEPROM. */
|
||
(type==13 && pcan_boot_id_device_type(id)==7 && pcan_boot_id_device_id(id)==15 &&
|
||
pcan_boot_id_msg_body(id)==0))) { ECanaRegs.CANRMP.all=mask; return; }
|
||
if(next==tail) overflow=1;
|
||
else {
|
||
queue[head].id=box->MSGID.all&0x1FFFFFFFUL;
|
||
queue[head].dlc=box->MSGCTRL.bit.DLC;
|
||
lo=box->MDL.all; hi=box->MDH.all;
|
||
for(i=0;i<4;++i) { queue[head].data[i]=(lo>>(8U*i))&255U;
|
||
queue[head].data[i+4]=(hi>>(8U*i))&255U; }
|
||
head=next;
|
||
}
|
||
ECanaRegs.CANRMP.all=mask;
|
||
}
|
||
|
||
void F28335CanBoot_RxISR(void)
|
||
{
|
||
if(!addressed) return; /* Неверный или ещё не настроенный номер PERIPH не должен принимать даже оставшиеся флаги RX. */
|
||
receive_box(&ECanaMboxes.MBOX3,8UL);
|
||
receive_box(&ECanaMboxes.MBOX6,64UL);
|
||
}
|
||
/* Вызывать после инициализации параметров, датчиков и прикладного режима.
|
||
* От этого момента отсчитывается интервал наблюдения перед авто-CONFIRM. */
|
||
void F28335CanBoot_Ready(void)
|
||
{ ready=1; ready_at=F28335Boot_Now(); }
|
||
/* Используется таймерными ISR приложения. Во время пробы кормление watchdog
|
||
* должно доказывать выполнение main: работа одного ISR не доказывает готовность.
|
||
* Старый старт без handoff сохраняет прежнюю политику watchdog. */
|
||
unsigned int F28335CanBoot_WatchdogAllowed(void)
|
||
{ return F28335Boot_RunningSlot()>1 || confirmed; }
|
||
/* Неблокирующий диспетчер относительно ожидания CAN: вызывать в каждом проходе main.
|
||
* Сами callbacks проверки/записи EEPROM синхронные. Порядок здесь важен:
|
||
* проба приложения -> завершение TX -> reset -> poll -> ошибки RX -> один кадр. */
|
||
unsigned int F28335CanBoot_Active(void) { return addressed && (boot.state!=PCAN_BOOT_STATE_IDLE || reset_requested); } /* Открытый ENTER_BOOT удерживает recovery; диагностический PING не меняет приоритет Flash и EEPROM. */
|
||
void F28335CanBoot_Service(void)
|
||
{
|
||
uint32_t now=F28335Boot_Now();
|
||
if(!addressed) return; /* Не обслуживает старую RAM-сессию после отказа повторной инициализации адреса. */
|
||
if(!F28335_BOOT_LOADER && ready && !confirmed && healthy && F28335Boot_RunningSlot()<2) {
|
||
/* A trial must keep executing main, not merely a timer ISR. */
|
||
/* После 100 проходов main и 5 секунд пробуем сохранить CONFIRM.
|
||
* Если до 10 секунд сохранение не удалось, main перестаёт кормить watchdog,
|
||
* что позволяет следующему старту откатиться к подтверждённому образу. */
|
||
if((uint32_t)(now-ready_at)<10000UL) ServiceDog();
|
||
if(main_passes<100) ++main_passes;
|
||
if(main_passes==100 && (uint32_t)(now-ready_at)>=5000UL) (void)confirm(0);
|
||
}
|
||
if(tx_busy) {
|
||
/* После CANTRR ждём завершения отмены или запоздавшего ACK, прежде чем
|
||
* повторно использовать mailbox. reset_requested к этому моменту уже снят,
|
||
* поэтому позднее подтверждение не должно перезапустить устройство. */
|
||
if(tx_canceling) {
|
||
if(!((ECanaRegs.CANAA.all|ECanaRegs.CANTA.all)&16UL)) return;
|
||
ECanaRegs.CANAA.all=16UL; ECanaRegs.CANTA.all=16UL;
|
||
tx_busy=tx_canceling=0;
|
||
} else if((uint32_t)(now-sent_at)>250UL) {
|
||
/* Cancel reset immediately, even if a late CAN ACK races CANTRR. */
|
||
reset_requested=0; pcan_boot_abort(&boot); tail=head;
|
||
tx_canceling=1; ECanaRegs.CANTRR.all=16UL; return;
|
||
} else if(ECanaRegs.CANTA.all&16UL) {
|
||
ECanaRegs.CANTA.all=16UL; tx_busy=0;
|
||
} else return;
|
||
}
|
||
if(reset_requested) { F28335Boot_Reset(reset_session); return; }
|
||
pcan_boot_poll(&boot,now);
|
||
if(tx_busy) return;
|
||
/* После потери кадра нельзя молча продолжать запись: сбрасываем RAM-сессию
|
||
* и накопленную очередь. Подтверждённый образ и опубликованный pending сохраняются. */
|
||
if(overflow || (ECanaRegs.CANRML.all&72UL)) {
|
||
overflow=0; ECanaRegs.CANRML.all=72UL;
|
||
pcan_boot_abort(&boot); tail=head; return;
|
||
}
|
||
if(tail!=head) {
|
||
pcan_boot_process(&boot,queue[tail].id,queue[tail].data,queue[tail].dlc);
|
||
tail=(tail+1U)&31U;
|
||
}
|
||
}
|
||
/* Используем session из NOINIT только для нового ENTER_BOOT после reset.
|
||
* Это отправка отложенного ACK готовности, а не восстановление принятых блоков
|
||
* или CRC незавершённой передачи: данные сессии через reset не сохраняются. */
|
||
void F28335CanBoot_Resume(unsigned int session)
|
||
{
|
||
if(addressed && F28335_BOOT_LOADER && session && session<256) /* Восстанавливает только допустимую сессию адресованного загрузчика после reset. */
|
||
(void)pcan_boot_process(&boot,pcan_boot_make_id(1,0, /* ACK относится к тому же физическому PERIPH, который принимал ENTER_BOOT в приложении. */
|
||
boot.config.device_type,boot.config.device_id,9,(session<<8)|2),0,0); /* MsgType 9, команда 2 и исходный SessionID сохраняют протокол handoff. */
|
||
}
|
||
#else
|
||
void F28335CanBoot_EarlyInit(void) {}
|
||
void F28335CanBoot_Init(unsigned int periph_id) { (void)periph_id; } /* Сборка без M95M02 не занимает CAN-адрес и не открывает сервис записи. */
|
||
void F28335CanBoot_RxISR(void) {}
|
||
void F28335CanBoot_Service(void) {}
|
||
unsigned int F28335CanBoot_Active(void) { return 0U; } /* Профиль без M95M02 не создаёт CAN-сеанс и не удерживает старт общего загрузчика. */
|
||
void F28335CanBoot_Ready(void) {}
|
||
unsigned int F28335CanBoot_WatchdogAllowed(void) { return 1; }
|
||
void F28335CanBoot_Resume(unsigned int session) { (void)session; }
|
||
#endif
|