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:
@@ -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" */
|
||||
|
||||
Reference in New Issue
Block a user