feat(app): 1.6.39 —— 1.6.29 起未上板业务改动入库,启动链与已提交基线对齐
主体为工作区累积的业务改动(CAN/PDO/桥/SSC/task_manager 等,详目见 config.h 版本注释链),新增 CoE 0x100A 真版本上报与 0x2502 APP→BOOT 总线交接。启动链(startup/SystemInit/initial/systick/hardfault)与 HEAD 逐字节一致;1.6.35~38 的跳转诊断实验(3M 化/BKP 面包屑/ haltWithWatchdog)未提交、已整体回退,补丁存 _revert_backup_20260916/。 真机实测(配对 BOOT 1.1.0.18):刷写 warm jump 与 reset cold jump 均可。
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
|
||||
volatile uint8_t EthercatCanBridge::s_lastSeq = 0U;
|
||||
volatile bool EthercatCanBridge::s_seqPrimed = false;
|
||||
volatile bool EthercatCanBridge::s_coldStartPending = false;
|
||||
volatile uint8_t EthercatCanBridge::s_lastTxResult = 0U;
|
||||
volatile uint16_t EthercatCanBridge::s_txCount = 0U;
|
||||
volatile uint16_t EthercatCanBridge::s_dropCount = 0U;
|
||||
@@ -223,9 +224,10 @@ bool EthercatCanBridge::peekOutput(const uint8_t *head)
|
||||
/* WIN@8 不再使用:1.6.21 电机整轮密排,无滑窗。 */
|
||||
|
||||
if (!s_seqPrimed) {
|
||||
s_seqPrimed = true;
|
||||
s_lastSeq = seq;
|
||||
return false;
|
||||
s_seqPrimed = true;
|
||||
s_lastSeq = seq;
|
||||
s_coldStartPending = true;
|
||||
return true; /* 冷启动首包强制全量读,避免漏掉首帧命令 */
|
||||
}
|
||||
|
||||
if (seq != s_lastSeq) {
|
||||
@@ -234,6 +236,13 @@ bool EthercatCanBridge::peekOutput(const uint8_t *head)
|
||||
return (head[ECAT_CAN_TX_OFF_FLAGS] & 0x01U) != 0U;
|
||||
}
|
||||
|
||||
void EthercatCanBridge::resetSequenceState()
|
||||
{
|
||||
s_seqPrimed = false;
|
||||
s_lastSeq = 0U;
|
||||
s_coldStartPending = false;
|
||||
}
|
||||
|
||||
void EthercatCanBridge::processOutput(const uint8_t *out)
|
||||
{
|
||||
if (out == nullptr) {
|
||||
@@ -243,19 +252,33 @@ void EthercatCanBridge::processOutput(const uint8_t *out)
|
||||
const uint8_t seq = out[ECAT_CAN_TX_OFF_SEQ];
|
||||
|
||||
if (!s_seqPrimed) {
|
||||
s_seqPrimed = true;
|
||||
s_lastSeq = seq;
|
||||
return;
|
||||
s_seqPrimed = true;
|
||||
s_lastSeq = seq;
|
||||
s_coldStartPending = true;
|
||||
}
|
||||
|
||||
/* 无论 seq 是否变化,优先处理非 seq 驱动的命令(FLAGS bit0 清统计) */
|
||||
if ((out[ECAT_CAN_TX_OFF_FLAGS] & 0x01U) != 0U) {
|
||||
s_txCount = 0U;
|
||||
s_dropCount = 0U;
|
||||
s_rxCount = 0U;
|
||||
s_txCount = 0U;
|
||||
s_dropCount = 0U;
|
||||
s_rxCount = 0U;
|
||||
s_rxPublished = true;
|
||||
}
|
||||
|
||||
if (seq == s_lastSeq) {
|
||||
bool executeCommand = false;
|
||||
if (s_coldStartPending) {
|
||||
s_coldStartPending = false;
|
||||
/* 冷启动首包:若包含有效单帧命令或批量帧,立即执行 */
|
||||
const uint8_t dlc = out[ECAT_CAN_TX_OFF_DLC];
|
||||
const uint8_t batchCount = out[ECAT_TXB_OFF_COUNT];
|
||||
if (dlc > 0U || batchCount > 0U) {
|
||||
executeCommand = true;
|
||||
}
|
||||
} else if (seq != s_lastSeq) {
|
||||
executeCommand = true;
|
||||
}
|
||||
|
||||
if (!executeCommand) {
|
||||
return;
|
||||
}
|
||||
s_lastSeq = seq;
|
||||
@@ -384,4 +407,9 @@ void EcatCan_BuildInput(uint8_t *in)
|
||||
EthercatCanBridge::buildInput(in);
|
||||
}
|
||||
|
||||
void EcatCan_ResetSequenceState(void)
|
||||
{
|
||||
EthercatCanBridge::resetSequenceState();
|
||||
}
|
||||
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -199,6 +199,9 @@ public:
|
||||
*/
|
||||
static bool peekOutput(const uint8_t *head);
|
||||
|
||||
/** @brief 复位输出 sequence 与冷启动同步状态。 */
|
||||
static void resetSequenceState();
|
||||
|
||||
static void processOutput(const uint8_t *out);
|
||||
|
||||
/**
|
||||
@@ -227,6 +230,7 @@ private:
|
||||
|
||||
static volatile uint8_t s_lastSeq;
|
||||
static volatile bool s_seqPrimed;
|
||||
static volatile bool s_coldStartPending;
|
||||
static volatile uint8_t s_lastTxResult;
|
||||
static volatile uint16_t s_txCount;
|
||||
static volatile uint16_t s_dropCount;
|
||||
@@ -259,6 +263,8 @@ extern "C" {
|
||||
void EcatCan_ProcessOutput(const uint8_t *out);
|
||||
/** @brief 供 dc_app.c 调用的 shim:填充输入 PDO。 */
|
||||
void EcatCan_BuildInput(uint8_t *in);
|
||||
/** @brief 供 dc_app.c 调用的 shim:复位输出 sequence 状态机。 */
|
||||
void EcatCan_ResetSequenceState(void);
|
||||
}
|
||||
|
||||
#endif /* COMM_ETHERCAT_CAN_BRIDGE_H */
|
||||
|
||||
@@ -4,9 +4,12 @@
|
||||
*/
|
||||
#include "comm/ethercat_pdo.h"
|
||||
#include "comm/ethercat_can_bridge.h"
|
||||
#include "comm/esc_exmc_bus.h"
|
||||
#include "device/canfd.h"
|
||||
#include "sys/config.h"
|
||||
#include "sys/cpu_load.h"
|
||||
#include "RTE_Components.h"
|
||||
#include CMSIS_device_header /* __DMB() */
|
||||
/* [DIFF-STATS] 临时仪表挂接(删除:去掉本块与 process() 内的 observe 块)。
|
||||
* 上一行的 sys/config.h 必须在此之前:宏未定义时 #if 会静默按 0 求值,
|
||||
* 开关就会被悄悄当成"关掉"。 */
|
||||
@@ -23,6 +26,12 @@ alignas(4) uint8_t EthercatPdo::s_in[EthercatPdo::kPdoInBytes] = {0};
|
||||
/* 输入 PDO 的工作缓冲:buildInput 填这里,填完整块发布到 s_in(见 process())。
|
||||
* 读侧 writeInputs 只读 s_in,因此永远看不到"填到一半"的镜像。 */
|
||||
alignas(4) uint8_t EthercatPdo::s_build[EthercatPdo::kPdoInBytes] = {0};
|
||||
|
||||
namespace {
|
||||
/* 待交回主站的输入数据双缓冲(ping-pong,配合 __DMB() 保证原子发布与 tear-free 读写) */
|
||||
alignas(4) uint8_t s_inBuf[2][EthercatPdo::kPdoInBytes] = {{0}, {0}};
|
||||
volatile uint8_t s_inActiveIdx = 0U;
|
||||
}
|
||||
alignas(4) uint8_t EthercatPdo::s_escSh1[32] = {0};
|
||||
alignas(4) uint8_t EthercatPdo::s_escSh2[ECAT_XRX_OFF_CH - 32U] = {0};
|
||||
alignas(4) uint8_t EthercatPdo::s_escSh3[kEcatPdoInBytes - ECAT_XRX_OFF_CH] = {0};
|
||||
@@ -90,16 +99,24 @@ void EthercatPdo::process()
|
||||
* 内层被门挡住后,它随后的 PDO_InputMapping() 仍会去读 s_in,若 buildInput
|
||||
* 直接写 s_in,读到的就是外层写到一半的半成品。填在别处、整块发布之后,
|
||||
* 读侧要么拿到上一份完整镜像、要么拿到这一份完整镜像,不存在中间态。 */
|
||||
/* 双缓冲发布:填入非活跃槽 nextIdx,完成后通过 __DMB() 与 s_inActiveIdx 切换
|
||||
* 实现原子发布,彻底避免中断读侧(PDI_Isr / Sync0_Isr)与主循环之间的读写撕裂。 */
|
||||
const uint8_t nextIdx = 1U - s_inActiveIdx;
|
||||
uint8_t *build = s_inBuf[nextIdx];
|
||||
|
||||
EthercatCanBridge::processOutput(s_out);
|
||||
EthercatCanBridge::buildInput(s_build);
|
||||
fillDiagTail(s_build);
|
||||
memcpy(s_in, s_build, kPdoInBytes);
|
||||
EthercatCanBridge::buildInput(build);
|
||||
fillDiagTail(build);
|
||||
|
||||
__DMB();
|
||||
s_inActiveIdx = nextIdx;
|
||||
memcpy(s_in, build, kPdoInBytes);
|
||||
|
||||
#if CONFIG_ECAT_DIFF_STATS
|
||||
/* [DIFF-STATS] 只测量:与上一份已发布镜像比对并累计到 1s 窗口,经 0x2000
|
||||
* sub13..23 上报。不改变上面的整块写行为,因此本版本固件的输出与 1.6.16
|
||||
* 逐字节一致。仪表自持上一份副本,这里不需要额外缓冲。 */
|
||||
EthercatDiffStats::observe(s_in);
|
||||
EthercatDiffStats::observe(s_inBuf[nextIdx]);
|
||||
#endif
|
||||
|
||||
s_building = false;
|
||||
@@ -159,7 +176,9 @@ void EthercatPdo::writeInputs(uint8_t *esc)
|
||||
if (esc == nullptr) {
|
||||
return;
|
||||
}
|
||||
memcpy(esc, s_in, kPdoInBytes);
|
||||
const uint8_t activeIdx = s_inActiveIdx;
|
||||
__DMB();
|
||||
memcpy(esc, s_inBuf[activeIdx], kPdoInBytes);
|
||||
++s_inputCycles;
|
||||
}
|
||||
|
||||
@@ -174,10 +193,13 @@ bool EthercatPdo::sparseEscWritePlan(uint16_t *off0, uint16_t *len0,
|
||||
#else
|
||||
/* 仅 1.6.21 密排布局:count 有效且两段之间确有空洞才分段。
|
||||
* magic 不对 / count 越界 / count 撑满到与段3相接 → 整包,避免陈旧空洞被误读。 */
|
||||
if (s_in[ECAT_MOTW_OFF_MAGIC] != kEcatMotLayoutMagic) {
|
||||
const uint8_t activeIdx = s_inActiveIdx;
|
||||
__DMB();
|
||||
const uint8_t *cur = s_inBuf[activeIdx];
|
||||
if (cur[ECAT_MOTW_OFF_MAGIC] != kEcatMotLayoutMagic) {
|
||||
return false;
|
||||
}
|
||||
const uint8_t count = s_in[ECAT_MOTW_OFF_COUNT];
|
||||
const uint8_t count = cur[ECAT_MOTW_OFF_COUNT];
|
||||
if (count > kEcatMotSlotsPerPdo) {
|
||||
return false;
|
||||
}
|
||||
@@ -253,19 +275,25 @@ uint8_t EthercatPdo::escWritePlan(uint16_t *offs, uint16_t *lens)
|
||||
return n;
|
||||
#else
|
||||
/* 三区差分:段1 桥状态 / 段2 电机密排(稀疏写有效长) / 段3+5 快照诊断。
|
||||
* 段1 计数器几乎每拍变,段2 稳定时省最大头,段3+5 多数拍稳定。 */
|
||||
* 段1 计数器几乎每拍变,段2 稳定时省最大头,段3+5 多数拍稳定。
|
||||
* 无论各段是否变化,必须保证每周期必定写入 SM3 结束地址 (kPdoInBytes - 1),
|
||||
* 以触发 LAN9252 3-buffer 提交换冲,否则主站将读到冻结的旧缓冲。 */
|
||||
const uint8_t activeIdx = s_inActiveIdx;
|
||||
__DMB();
|
||||
const uint8_t *cur = s_inBuf[activeIdx];
|
||||
|
||||
uint8_t n = 0U;
|
||||
if (diffNeedWrite(s_in, 0U, 32U, s_escSh1, s_escSh1Valid, s_escFlush1)) {
|
||||
if (diffNeedWrite(cur, 0U, 32U, s_escSh1, s_escSh1Valid, s_escFlush1)) {
|
||||
offs[n] = 0U;
|
||||
lens[n] = 32U;
|
||||
++n;
|
||||
}
|
||||
if (diffNeedWrite(s_in, 32U, (uint16_t)(ECAT_XRX_OFF_CH - 32U),
|
||||
if (diffNeedWrite(cur, 32U, (uint16_t)(ECAT_XRX_OFF_CH - 32U),
|
||||
s_escSh2, s_escSh2Valid, s_escFlush2)) {
|
||||
offs[n] = 32U;
|
||||
/* 写长:稀疏时只写到 count*56,但影子比对的是完整 32..543(含清零空洞)。
|
||||
* 空洞不写入 ESC 无妨:主站只按 count 迭代。 */
|
||||
const uint16_t wlen = escSeg2WriteLen(s_in);
|
||||
const uint16_t wlen = escSeg2WriteLen(cur);
|
||||
lens[n] = (wlen > 32U) ? (uint16_t)(wlen - 32U) : 0U;
|
||||
if (lens[n] == 0U) {
|
||||
/* count=0:段2 头 8 字节仍要写(count/active/magic) */
|
||||
@@ -273,16 +301,19 @@ uint8_t EthercatPdo::escWritePlan(uint16_t *offs, uint16_t *lens)
|
||||
}
|
||||
++n;
|
||||
}
|
||||
if (diffNeedWrite(s_in, ECAT_XRX_OFF_CH, (uint16_t)(kPdoInBytes - ECAT_XRX_OFF_CH),
|
||||
bool seg3Included = false;
|
||||
if (diffNeedWrite(cur, ECAT_XRX_OFF_CH, (uint16_t)(kPdoInBytes - ECAT_XRX_OFF_CH),
|
||||
s_escSh3, s_escSh3Valid, s_escFlush3)) {
|
||||
offs[n] = ECAT_XRX_OFF_CH;
|
||||
lens[n] = (uint16_t)(kPdoInBytes - ECAT_XRX_OFF_CH);
|
||||
++n;
|
||||
seg3Included = true;
|
||||
}
|
||||
if (n == 0U) {
|
||||
offs[0] = (uint16_t)(kPdoInBytes - 2U);
|
||||
lens[0] = 2U;
|
||||
n = 1U;
|
||||
if (!seg3Included) {
|
||||
/* 段 3 未包含时,补写末 2 字节以保证触碰 SM3 结束地址触发硬件提交 */
|
||||
offs[n] = (uint16_t)(kPdoInBytes - 2U);
|
||||
lens[n] = 2U;
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
#endif
|
||||
@@ -348,4 +379,25 @@ uint8_t EcatPdo_EscWritePlan(uint16_t *offs, uint16_t *lens, uint8_t maxSegs)
|
||||
return n;
|
||||
}
|
||||
|
||||
/* 1.6.34: HBI/EXMC 事务互斥(仅屏蔽 EXTI0/1/2,不关全局中断) */
|
||||
uint32_t EscExmcBus_MaskEnter(void)
|
||||
{
|
||||
return EscExmcBus::maskEnter();
|
||||
}
|
||||
|
||||
void EscExmcBus_MaskExit(uint32_t prev)
|
||||
{
|
||||
EscExmcBus::maskExit(prev);
|
||||
}
|
||||
|
||||
uint32_t HW_EscExmcMaskEnter(void)
|
||||
{
|
||||
return EscExmcBus::maskEnter();
|
||||
}
|
||||
|
||||
void HW_EscExmcMaskExit(uint32_t prev)
|
||||
{
|
||||
EscExmcBus::maskExit(prev);
|
||||
}
|
||||
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -166,6 +166,10 @@ extern void EcatPdo_ReadOutputs(const UINT8 *esc);
|
||||
extern void EcatPdo_Process(void);
|
||||
extern void EcatPdo_WriteInputs(UINT8 *esc);
|
||||
|
||||
/* 0x2502 用:进 BOOT 的异步请求(sys/ota_boot_request.cpp 的 extern "C" shim)。
|
||||
* 只置 pending 标志,真正的配置页写入 + 复位在主循环下一圈,见 ObjBootReqWrite 注。 */
|
||||
extern void EcatEnterBootRequestAsync(void);
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* PDO Assign 对象 (0x1C12 / 0x1C13)
|
||||
*---------------------------------------------------------------------------*/
|
||||
@@ -298,6 +302,57 @@ OBJCONST TSDOINFOENTRYDESC OBJMEM asSyncDiagEntryDesc[] = {
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* sub23 : [DIFF-STATS] 比对自身 DWT 周期 */
|
||||
};
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* 0x2502 引导程序交接请求(主站 → APP:「让你回 BOOT」)
|
||||
*
|
||||
* 为什么要有它:EtherCAT 直灌的会话对象在 **BOOT** 里(0x2500/0x2501),APP 的字典
|
||||
* 根本没有这两个对象 —— 实测对着 APP 写 0x2500 回 abort=0x06020000「对象不存在」。
|
||||
* 所以一台正在跑 APP 的机器必须先交接,才能走总线刷写。量产那条交接只有
|
||||
* SPI 0xB0 和 CAN 0x320+"OTA!",没有总线内的入口;常驻刷写必须补上。
|
||||
*
|
||||
* 语义:写 1 = 请求进 BOOT;写 0 是无操作(不做"取消",避免半路误清)。
|
||||
* 动作复用现成的 OtaBootRequest 异步链:这里**只置 pending 标志**,配置页三字的
|
||||
* Flash 写入 + NVIC_SystemReset 由主循环 OtaBootRequest::process() 下一圈执行
|
||||
* (sys/initial.cpp 每圈调;与 SPI 0xB0 在 ISR 里只置标志同一个形状),
|
||||
* 所以 SDO 响应先发出去、复位后到,不会把 SM1 半包留在总线上。
|
||||
*
|
||||
* 交接后板子会稳定停在 BOOT:APP 写的是 EN=NOT_AVAL,而 BOOT 的救砖门
|
||||
* ecatArmed() = !(CHECK==AVAL && EN==AVAL) ⇒ 顺手就把 0x2500 直灌解锁了,
|
||||
* 不需要 word8 令牌(也别依赖它:APP 的 otaConfigSet 只保全 8 个字,一擦页就没了)。
|
||||
*---------------------------------------------------------------------------*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM sEntryDesc0x2502[] = {
|
||||
/* VAR 只有 :00 一个条目,且必须带写权限位(SSC 的通用写权限门在 objdef.c:1325,
|
||||
* 早于 1352 的回调分发 —— BOOT 的 0x2500 就是栽在只给 ACCESS_READ 上) */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READWRITE},
|
||||
};
|
||||
static OBJCONST UCHAR aName0x2502[] = "GD32 boot request";
|
||||
|
||||
static UINT32 s_bootReq = 0U; /**< 最近一次写入值,读回用于主站自证 */
|
||||
|
||||
static UINT8 ObjBootReqWrite(UINT16 index, UINT8 subindex, UINT32 dataSize,
|
||||
UINT16 MBXMEM *pData, UINT8 bCompleteAccess)
|
||||
{
|
||||
const UINT8 *p8 = (const UINT8 *)pData;
|
||||
UINT32 v;
|
||||
|
||||
(void)index;
|
||||
(void)bCompleteAccess;
|
||||
|
||||
if (subindex != 0U) {
|
||||
return ABORTIDX_SUBINDEX_NOT_EXISTING;
|
||||
}
|
||||
if (pData == NULL || dataSize < 4U) {
|
||||
return ABORTIDX_PARAM_LENGTH_TOO_SHORT;
|
||||
}
|
||||
v = (UINT32)p8[0] | ((UINT32)p8[1] << 8) | ((UINT32)p8[2] << 16) | ((UINT32)p8[3] << 24);
|
||||
s_bootReq = v;
|
||||
|
||||
if (v == 1U) {
|
||||
EcatEnterBootRequestAsync();
|
||||
}
|
||||
return 0U;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* Object Names
|
||||
*---------------------------------------------------------------------------*/
|
||||
@@ -343,6 +398,10 @@ TOBJECT OBJMEM ApplicationObjDicList[] = {
|
||||
{NULL, NULL, 0x2000, {DEFTYPE_RECORD, 23 | (OBJCODE_REC << 8)},
|
||||
asSyncDiagEntryDesc, aName0x2000, &s_syncDiag, NULL, NULL, 0x0000},
|
||||
|
||||
/* 0x2502: 引导程序交接请求(写 1 = 回 BOOT 等灌写;见 ObjBootReqWrite 注) */
|
||||
{NULL, NULL, 0x2502, {DEFTYPE_UNSIGNED32, 0 | (OBJCODE_VAR << 8)},
|
||||
sEntryDesc0x2502, aName0x2502, &s_bootReq, NULL, ObjBootReqWrite, 0x0000},
|
||||
|
||||
/* 终止条目 */
|
||||
{NULL, NULL, 0xFFFF, {0, 0}, NULL, NULL, NULL, NULL, NULL, 0x000}
|
||||
};
|
||||
@@ -391,8 +450,17 @@ UINT16 APPL_StopInputHandler(void) { return 0; }
|
||||
/*---------------------------------------------------------------------------
|
||||
* Output Handler (SAFEOP→OP)
|
||||
*---------------------------------------------------------------------------*/
|
||||
UINT16 APPL_StartOutputHandler(void) { return 0; }
|
||||
UINT16 APPL_StopOutputHandler(void) { return 0; }
|
||||
UINT16 APPL_StartOutputHandler(void)
|
||||
{
|
||||
EcatCan_ResetSequenceState();
|
||||
return 0;
|
||||
}
|
||||
|
||||
UINT16 APPL_StopOutputHandler(void)
|
||||
{
|
||||
EcatCan_ResetSequenceState();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* APPL_AckErrorInd
|
||||
@@ -691,7 +759,7 @@ void EcatDbg_OpInputFreshness(void)
|
||||
}
|
||||
|
||||
/* 强制刷新:先重开门/首输出标志(门关着时 PDI 永远不会回来),
|
||||
* 再在关中断窗口内做一次输入映射 —— 此路径极低频,关中断可接受。 */
|
||||
* 再在 EXMC 互斥窗口(仅屏蔽 EXTI0/1/2)内做一次输入映射,保证不关全局中断(Preempt 0 CAN RX 不受影响)。 */
|
||||
{
|
||||
UINT16 dummy = 0U;
|
||||
const UINT16 evt = HW_GetALEventRegister();
|
||||
@@ -703,9 +771,9 @@ void EcatDbg_OpInputFreshness(void)
|
||||
HW_EscReadWord(dummy, nEscAddrOutputData);
|
||||
HW_EscReadWord(dummy, (UINT16)(nEscAddrOutputData + nPdOutputSize - 2U));
|
||||
}
|
||||
__asm volatile ("cpsid i" ::: "memory");
|
||||
const uint32_t prevMask = EscExmcBus_MaskEnter();
|
||||
PDO_InputMapping();
|
||||
__asm volatile ("cpsie i" ::: "memory");
|
||||
EscExmcBus_MaskExit(prevMask);
|
||||
++s_opFreshRescues;
|
||||
s_inMapSeqSeen = s_inMapSeq;
|
||||
s_inStaleBaseCyc = EcatDbg_Dwt();
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "ecat_def.h"
|
||||
#include "esc.h"
|
||||
#include "ecatslv.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -36,6 +37,15 @@ void APPL_Application(void);
|
||||
void EcatDbg_OpInputFreshness(void);
|
||||
UINT32 EcatDbg_OpFreshRescues(void);
|
||||
|
||||
/* 1.6.34: HBI/EXMC 事务互斥(仅屏蔽 EXTI0/1/2,不关全局中断) */
|
||||
uint32_t EscExmcBus_MaskEnter(void);
|
||||
void EscExmcBus_MaskExit(uint32_t prev);
|
||||
uint32_t HW_EscExmcMaskEnter(void);
|
||||
void HW_EscExmcMaskExit(uint32_t prev);
|
||||
|
||||
/* 1.6.34: 复位输出 sequence 状态机 */
|
||||
void EcatCan_ResetSequenceState(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -666,8 +666,10 @@
|
||||
#define DEVICE_SW_VERSION "1.0"
|
||||
#endif
|
||||
|
||||
/* 0x100A 的位长。SSC 默认 3 字符只装得下占位 "1.0";这里抬到 16,真实值由
|
||||
* sys/initial.cpp 起板时写进 acSoftwareversion(与 BOOT 侧同款改动)。 */
|
||||
#ifndef DEVICE_SW_VERSION_LEN
|
||||
#define DEVICE_SW_VERSION_LEN 0x3
|
||||
#define DEVICE_SW_VERSION_LEN 0x10
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
|
||||
@@ -209,8 +209,27 @@ OBJCONST UCHAR OBJMEM aName0x1009[] = "Hardware version";
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x100A (Software version) variable to handle the object data
|
||||
*
|
||||
* 必须显式给到 DEVICE_SW_VERSION_LEN+1:条目描述符按 LEN 声明位长,SSC 会照长度
|
||||
* 整块回给主站;用 =DEVICE_SW_VERSION 推出来只有 4 字节,读到的是越界内容。
|
||||
* 真实版本号由 sys/initial.cpp 起板时调 AppSetSoftwareVersion() 覆盖。
|
||||
*/
|
||||
CHAR acSoftwareversion[] = DEVICE_SW_VERSION;
|
||||
CHAR acSoftwareversion[DEVICE_SW_VERSION_LEN + 1] = DEVICE_SW_VERSION;
|
||||
|
||||
/** @brief 覆盖 0x100A 上报的软件版本串(容量只有本文件知道,故 setter 放这儿)。 */
|
||||
void AppSetSoftwareVersion(const char *version)
|
||||
{
|
||||
unsigned int i = 0U;
|
||||
|
||||
if (version == NULL) {
|
||||
return;
|
||||
}
|
||||
/* 本文件没 include stdio,手写有界拷贝:超长就截断,末尾必带 NUL。 */
|
||||
for (; i + 1U < sizeof(acSoftwareversion) && version[i] != '\0'; i++) {
|
||||
acSoftwareversion[i] = version[i];
|
||||
}
|
||||
acSoftwareversion[i] = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief 0x1009 (Hardware version) entry description
|
||||
|
||||
Reference in New Issue
Block a user